I am trying to implement a settlement date algorithm to check for local public holidays in my region for a small trading application built in php/mysql. I have all local public holiday dates stored neatly in my db and then i output using a variable.
The application should check the current date and then add three working days (Minus weekends) as the settlement cycle in my country follows a t+3 settlement rule for all trades. This requires that if the settlement should fall on a public holiday or weekend, the application should compare the dates in the db table and then set a new working day as the settlement date.
can someone please help me with what i may be missing? Please find the code below:
<?php
$rsd = date ( 'Ymd' , strtotime ( '3 weekdays' ) );
$phd = $row_public_holidays['date'];
if ($rsd == $phd) {
echo date ( 'Ymd' , strtotime ( '4 weekdays' ) );
} else {
echo date ( 'Ymd' , strtotime ( '3 weekdays' ) );
}
?>
If you need the output of date to have 8 digits, you can update the format string from Ymj to Ymd. Changing the last character from j to d moves from showing:
Day of the month without leading zeros
to:
Day of the month, 2 digits with leading zeros
As per the documentation for date (http://php.net/manual/en/function.date.php).
Putting this together
The call to strtotime performs the calculation and returns an integer representing the seconds since epoch in the determined (or provided) timezone (http://php.net/manual/en/function.strtotime.php). The call to date is simply to format that integer into a string based on the provided format (http://php.net/manual/en/function.date.php).
So:
$time = strtotime('4 weekdays');
var_dump($time);
$date1 = date('Ymj', $time);
var_dump($date1);
$date2 = date('Ymd', $time);
var_dump($date2);
outputs:
int(1533600000)
string(7) "2018087"
string(8) "20180807"
In this example, the calls to date don't change the value of $time.
Related
I have SQLite DB one table contains datetime field
with datatype "timestamp" REAL value is 18696.0
attach image for table structure
So, I want this 18696.0 value to be converted into MySQL Y-m-d format and result should be 2021-03-10
I have didn't found any solution online. any help would be appreciated.
SQLite timestamp converted into MySQL timestamp.
EDIT: Thankyou for updating your question with the correct number and what date it should represent.
You can achieve what you need with a function that adds the days onto the Unix Epoch date:
function realDateToYmd($real, $outputFormat='Y-m-d')
{
$date = new DateTime('1970-01-01');
$date->modify('+' . intval($real) . ' days');
return $date->format($outputFormat);
}
echo realDateToYmd('18696.0');
// returns 2021-03-10
SQLite dates stored in REAL data type stores dates as a Julian Day.
From https://www.sqlite.org/datatype3.html
REAL as Julian day numbers, the number of days since noon in Greenwich on November 24, 4714 B.C. according to the proleptic Gregorian calendar.
PHP has a jdtogregorian function, in which one comment has a handy function to convert to ISO8601 dates:
function JDtoISO8601($JD) {
if ($JD <= 1721425) $JD += 365;
list($month, $day, $year) = explode('/', jdtogregorian($JD));
return sprintf('%+05d-%02d-%02d', $year, $month, $day);
}
echo JDtoISO8601('17889.0');
// Results in -4664-11-16
The results don't exactly look right, is it definitely 17889.0 in SQLite?
If this float number 18696.0 represents the number of days since 1970-01-01 then the date can also be calculated like this:
$days = 18696.0;
$dt = date_create('#'.((int)($days * 86400)));
$mysqlDate = $dt->format('Y-m-d'); //"2021-03-10"
background information
Or simply with gmdate:
$mySqlDate = gmdate('Y-m-d',$days*86400);
The days are simply converted into seconds to get a valid timestamp for gmdate.
Try this:
<?php
echo date('Y-m-d H:i:s', 17889);
?>
Output:
1970-01-01 04:58:09
I have date conversion issue: The goal is to determine an "order by date" for a book, this should be 13 days prior to the book's "release date", which is in EST. The "order by date" should display the 13 day time frame plus any time diff between the user's time and EST (New York) time. So in my function below, I'm getting the release date, NYC time, user's time and trying to do order_by_date = release_date - ( (nyc/user local time diff) + 13 days). It seemed to be working, but after testing this out with multiple release dates, I'm consistently returning a 14 day difference, not a 13 day one... My main question is why would the function below output a date that is 14 days before a release date and not 13 days? I've tried echoing each time variable and each one looks normal (i.e. for a user in NYC, the time diff is 0, but for someone on PST it's 3hour diff), I wonder if the formatting is having an effect on the value? Thanks for any input:
function get_order_by_date( ) {
$release_date = '26-02-2019 00:00:00'
$ny_timezone = new \DateTimeZone( 'America/New_York' );
$gmt_timezone = new \DateTimeZone( 'GMT' );
$user_date_time = new \DateTime( $release_date, $gmt_timezone );
$offset = $ny_timezone->getOffset( $user_date_time );
$my_interval = \DateInterval::createFromDateString( (string) $offset . 'seconds' );
$user_date_time->add( $my_interval );
$result = $user_date_time->format( 'd-m-Y H:i:s' );
$order_by_date = date( 'F jS', strtotime( $result . ' - 13 days' ) );
return $order_by_date;
}
It might be easier to see why we get a certain date as a result if we simplify the process a little. If I understand correctly the function needs to take the release date and do two things to it:
Shift 13 days prior to it
Set it to the user's timezone
If we start with the release date in the release timezone, making those modifications is more straightforward.
For the purposes of the answer I'm returning the result in a format that includes the time so we can see exactly where those modifications put the result, but you can use whatever format is needed.
<?php
function get_order_by_date(string $release_date, string $user_timezone)
{
$release_timezone = new \DateTimeZone( 'America/New_York' );
$user_timezone = new \DateTimeZone($user_timezone);
// start with the release date in NY time
$orderby_date = new \DateTime($release_date, $release_timezone);
// 13 days prior
$orderby_date->modify('-13 days');
// shift to the user's timezone
$orderby_date->setTimezone($user_timezone);
return $orderby_date->format('Y-m-d H:i:s');
}
Using the date in your example, 26-02-2019 00:00:00 moving thirteen days before would give you 13-02-2019 00:00:00.
At that time in NY, the time in LA would be three hours earlier, so the result would be in the previous day
echo get_order_by_date('26-02-2019 00:00:00', 'America/Los_Angeles'); // 2019-02-12 21:00:00
And the time in GMT would be five hours later, so the result would be in the same day
echo get_order_by_date('26-02-2019 00:00:00', 'GMT'); // 2019-02-13 05:00:00
I want to add an x number of week days (e.g. 48 weekday hours) to the current timestamp. I am trying to do this using the following
echo (strtotime('2 weekdays');
However, this doesn't seem to take me an exact 48 hours ahead in time. For example, inputting the current server time of Tuesday 18/03/2014 10:47 returns Thursday 20/03/2014 00:00. using the following function:
echo (strtotime('2 weekdays')-mktime())/86400;
It can tell that it's returning only 1.3 weekdays from now.
Why is it doing this? Are there any existing functions which allow an exact amount of weekday hours?
Given you want to preserve the weekdays functionality and not loose the hours, minutes and seconds, you could do this:
$now = new DateTime();
$hms = new DateInterval(
'PT'.$now->format('H').'H'.
$now->format('i').'M'.
$now->format('s').'S'.
);
$date = new DateTime('2 weekdays');
$date->add($hms);//add hours here again
The reason why weekday doesn't add the hours is because, if you add 1 weekday at any point in time on a monday, the next weekday has to be tuesday.
The hour simply does not matter. Say your date is 2014-01-02 12:12:12, and you want the next weekday, that day starts at 2014-01-03 00:00:00, so that's what you get.
My last solution works though, and here's how: I use the $now instance of DateTime, and its format method to construct a DateInterval format string, to be passed to the constructor. An interval format is quite easy: it starts with P, for period, then a digit and a char to indicate what that digit represents: 1Y for 1 Year, and 2D for 2 Days.
However, we're only interested in hours, minutes and seconds. Actual time, which is indicated using a T in the interval format string, hence we start the string with PT (Period Time).
Using the format specifiers H, i and s, we construct an interval format that in the case of 12:12:12 looks like this:
$hms = new DateInterval(
'PT12H12M12S'
);
Then, it's a simple matter of calling the DateTime::add method to add the hours, minutes and seconds to our date + weekdays:
$weekdays = new DateTime('6 weekdays');
$weekdays->add($hms);
echo $weekdays->format('Y-m-d H:i:s'), PHP_EOL;
And you're there.
Alternatively, you could just use the basic same trick to compute the actual day-difference between your initial date, and that date + x weekdays, and then add that diff to your initial date. It's the same basic principle, but instead of having to create a format like PTXHXMXS, a simple PXD will do.
Working example here
I'd urge you to use the DateInterface classes, as it is more flexible, allows for type-hinting to be used and makes dealing with dates just a whole lot easier for all of us. Besides, it's not too different from your current code:
$today = new DateTime;
$tomorrow = new DateTime('tomorrow');
$dayAfter = new DateTime('2 days');
In fact, it's a lot easier if you want to do frequent date manipulations on a single date:
$date = new DateTime();//or DateTime::createFromFormat('Y-m-d H:i:s', $dateString);
$diff = new DateInterval('P2D');//2 days
$date->add($diff);
echo $date->format('Y-m-d H:i:s'), PHP_EOL, 'is the date + 2 days', PHP_EOL;
$date->sub($diff);
echo $date->format('Y-m-d H:i:s'), PHP_EOL, 'was the original date, now restored';
Easy, once you've spent some time browsing through the docs
I think I have found a solution. It's primitive but after some quick testing it seems to work.
The function calculates the time passed since midnight of the current day, and adds it onto the date returned by strtotime. Since this could fall into a weekend day, I've checked and added an extra day or two accordingly.
function weekDays($days) {
$tstamp = (strtotime($days.' weekdays') + (time() - strtotime("today")));
if(date('D',$tstamp) == 'Sat') {
$tstamp = $tstamp + 86400*2;
}
elseif(date('D',$tstamp) == 'Sun') {
$tstamp = $tstamp + 86400;
}
return $tstamp;
}
Function strtotime('2 weekdays') seems to add 2 weekdays to the current date without the time.
If you want to add 48 hours why not adding 2*24*60*60 to mktime()?
echo(date('Y-m-d', mktime()+2*24*60*60));
The currently accepted solution works, but it will fail when you want to add weekdays to a timestamp that is not now. Here's a simpler snippet that will work for any given point in time:
$start = new DateTime('2021-09-29 15:12:10');
$start->add(date_interval_create_from_date_string('+ 3 weekdays'));
echo $start->format('Y-m-d H:i:s'); // 2021-10-04 15:12:10
Note that this will also work for a negative amount of weekdays:
$start = new DateTime('2021-09-29 15:12:10');
$start->add(date_interval_create_from_date_string('- 3 weekdays'));
echo $start->format('Y-m-d H:i:s'); // 2021-09-24 15:12:10
I'm trying to use DateTime to check if a credit card expiry date has expired but I'm a bit lost.
I only want to compare the mm/yy date.
Here is my code so far
$expmonth = $_POST['expMonth']; //e.g 08
$expyear = $_POST['expYear']; //e.g 15
$rawExpiry = $expmonth . $expyear;
$expiryDateTime = \DateTime::createFromFormat('my', $rawExpiry);
$expiryDate = $expiryDateTime->format('m y');
$currentDateTime = new \DateTime();
$currentDate = $currentDateTime->format('m y');
if ($expiryDate < $currentDate) {
echo 'Expired';
} else {
echo 'Valid';
}
I feel i'm almost there but the if statement is producing incorrect results. Any help would be appreciated.
It's simpler than you think. The format of the datess you are working with is not important as PHP does the comparison internally.
$expires = \DateTime::createFromFormat('my', $_POST['expMonth'].$_POST['expYear']);
$now = new \DateTime();
if ($expires < $now) {
// expired
}
You can use the DateTime class to generate a DateTime object matching the format of your given date string using the DateTime::createFromFormat() constructor.
The format ('my') would match any date string with the string pattern 'mmyy', e.g. '0620'. Or for dates with 4 digit years use the format 'mY' which will match dates with the following string pattern 'mmyyyy', e.g. '062020'. It's also sensible to specify the timezone using the DateTimeZone class.
$expiryMonth = 06;
$expiryYear = 20;
$timezone = new DateTimeZone('Europe/London');
$expiryTime = \DateTime::createFromFormat('my', $expiryMonth.$expiryYear, $timezone);
See the DateTime::createFromFormat page for more formats.
However - for credit/debit card expiry dates you will also need to take into account the full expiry DATE and TIME - not just the month and year.
DateTime::createFromFormat will by default use todays day of the month (e.g. 17) if it is not specified. This means that a credit card could appear expired when it still has several days to go. If a card expires 06/20 (i.e. June 2020) then it actually stops working at 00:00:00 on 1st July 2020. The modify method fixes this. E.g.
$expiryTime = \DateTime::createFromFormat('my', $expiryMonth.$expiryYear, $timezone)->modify('+1 month first day of midnight');
The string '+1 month first day of midnight' does three things.
'+1 month' - add one month.
'first day of' - switch to the first day of the month
'midnight' - change the time to 00:00:00
The modify method is really useful for many date manipulations!
So to answer the op, this is what you need — with a slight adjustment to format to cater for single digit months:
$expiryMonth = 6;
$expiryYear = 20;
$timezone = new DateTimeZone('Europe/London');
$expiryTime = \DateTime::createFromFormat(
'm-y',
$expiryMonth.'-'.$expiryYear,
$timezone
)->modify('+1 month first day of midnight');
$currentTime = new \DateTime('now', $timezone);
if ($expiryTime < $currentTime) {
// Card has expired.
}
An addition to the above answers.
Be aware that by default the days will also be in the calculation.
For example today is 2019-10-31 and if you run this:
\DateTime::createFromFormat('Ym', '202111');
It will output 2021-12-01, because day 31 does not exist in November and it will add 1 extra day to your DateTime object with a side effect that you will be in the month December instead of the expected November.
My suggestion is always use the day in your code.
For op's question:
$y=15;
$m=05;
if(strtotime( substr(date('Y'), 0, 2)."{$y}-{$m}" ) < strtotime( date("Y-m") ))
{
echo 'card is expired';
}
For others with full year:
$y=2015;
$m=5;
if(strtotime("{$y}-{$m}") < strtotime( date("Y-m") ))
{
echo 'card is expired';
}
Would it not be simpler to just compare the string "201709" to the current year-month? Creating datetime objects will cost php some effort, I suppose.
if($_POST['expYear']. str_pad($_POST['expMonth'],2,'0', STR_PAD_LEFT ) < date('Ym')) {
echo 'expired';
}
edited as Adam states
The best answer is provided by John Conde above. It it does the minimum amount of processing: creates two correct DateTime objects, compares them and that's all it needs.
It could work also as you started but you must format the dates in a way that puts the year first.
Think a bit about it: as dates, 08/15 (August 2015) is after 12/14 (December 2014) but as strings, '08 15' is before '12 14'.
When the year is in front, even as strings the years are compared first and then, only when the years are equal the months are compared:
$expiryDate = $expiryDateTime->format('y m');
$currentDate = $currentDateTime->format('y m');
if ($expiryDate < $currentDate) {
echo 'Expired';
} else {
echo 'Valid';
}
Keep it simple, as the answer above me says except you need to string pad to the left:
isCardExpired($month, $year)
{
$expires = $year.str_pad($month, 2, '0', STR_PAD_LEFT);
$now = date('Ym');
return $expires < $now;
}
No need to add extra PHP load using DateTime
If you are using Carbon, which is a very popular Datetime extension library. Then this should be:
$expMonth = $_POST['month'];
$expYear = $_POST['year'];
$format_m_y = str_pad($expMonth,2,'0', STR_PAD_LEFT).'-'.substr($expYear, 2);
$date = \Carbon\Carbon::createFromFormat('m-y', $format_m_y)
->endOfMonth()
->startOfDay();
if ($date->isPast()) {
// this card is expired
}
Also take into consideration the exact date and time expiration:
Credit cards expire at the end of the month printed as its expiration date, not at the beginning. Many cards actually technically expire one day after the end of that month. In any case, unless they list a specific day of expiration along with month and year, they should work all the way through the end of their expiration month. Cardholders should not wait until the last moment to secure a replacement card. Source
I have a problem where I need to handle dates where the month and day parts are optional. For example, the year will always be known but sometimes the day or month and day will be unknown.
In MySQL I can create a table with a date field and while I can't find any reference in the MySQL Manual it will accept the following as valid:
(YYYY-MM-DD format):
2011-02-10 // Current date
2011-02-00 // Day unknown so replaced with 00
2011-00-00 // Day and month unkown so replaced with 00-00
Test calculations from within the database work fine so I can still sort results easily. In the manual it says that month needs to be between 01 and 12, and day between 01 and 31 - but it does accept 00.
First question: Am I going to run into trouble using 00 in the month or day parts or is this perfectly acceptable?
Next question: Is there a PHP function (or MySQL format command) that will automatically format the following dates into the required format string?
2011 becomes 2011-00-00
2011-02 becomes 2011-02-00
Or do I need write a special function to handle this?
The following doesn't work:
<?php
$date = date_create_from_format('Y-m-d', '2011-00-00');
echo date_format($date, 'Y-m-d');
// Returns 2010-11-30
$date = date_create_from_format('Y-m-d', '2011-02-00');
echo date_format($date, 'Y-m-d');
// Returns 2011-01-31
?>
Third question: Is there a PHP function (or MySQL command) to format the dates for use in PHP?
Finally, is this the best approach? Or is there a 'best practise' method?
EDIT:
Here is what I'm currently doing:
A date field can accept a date in the format YYYY, YYYY-MM, or YYYY-MM-DD and before sending to the database it is processed in this function:
/**
* Takes a date string in the form:
* YYYY or
* YYYY-MM or
* YYYY-MM-DD
* and validates it
*
* Use date_format($date, $format); to reverse.
*
* #param string $phpDate Date format [YYYY | YYYY-MM | YYYY-MM-DD]
*
* #return array 'date' as YYYY-MM-DD, 'format' as ['Y' | 'Y-m' | 'Y-m-d'] or returns false if invalid
*/
function date_php2mysql($phpDate) {
$dateArr = false;
// Pattern match
if (preg_match('%^(?P<year>\d{4})[- _/.]?(?P<month>\d{0,2})[- _/.]?(?P<day>\d{0,2})%im', trim($phpDate), $parts)) {
if (empty($parts['month'])) {
// Only year valid
$date = $parts['year']."-01-01";
$format = "Y";
} elseif (empty($parts['day'])) {
// Year and month valid
$date = $parts['year']."-".$parts['month']."-01";
$format = "Y-m";
} else {
// Year month and day valid
$date = $parts['year']."-".$parts['month']."-".$parts['day'];
$format = "Y-m-d";
}
// Double check that it is a valid date
if (strtotime($date)) {
// Valid date and format
$dateArr = array('date' => $date, 'format' => $format);
}
} else {
// Didn't match
// Maybe it is still a valid date
if (($timestamp = strtotime($phpDate)) !== false) {
$dateArr = array('date' => date('Y-m-d', $timestamp), 'format' => "Y-m-d");
}
}
// Return result
return $dateArr;
}
So it pattern matches the input $phpDate where it must begin with 4 digits, then optionally pairs of digits for the month and the day. These are stored in an array called $parts.
It then checks if months or days exist, specifying the format string and creating the date.
Finally, if everything checks out, it returns a valid date as well as a format string. Otherwise it returns FALSE.
I end up with a valid date format for my database and I have a way of using it again when it comes back out.
Anyone think of a better way to do this?
I have a problem where I need to handle dates where the month and day parts are optional.
For example, the year will always be known but sometimes the day or month and day will be
unknown.
In many occasions, we do need such 'more or less precise' dates, and I use such dates as 2011-04-01 (precise), as well as 2011-04 (= April 2011) and 2011 (year-only date) in archives metadata. As you mention it, MySQL date field tolerates '2011-00-00' though no FAQs tell about it, and it's fine.
But then, I had to interface the MySQL database via ODBC and the date fields
are correctly translated, except the 'tolerated' dates (Ex: '2011-04-00' results empty in the resulting MySQL-ODBC-connected ACCESS database.
For that reason, I came to the conclusion that the MySQL date field could be converted in a plain VARCHAR(10) field : As long as we don't need specific MySQL date functions, it works fine, and of course, we can still use php date functions and your fine date_php2mysql() function.
I would say that the only case when a MySQL date field is needed
is when one needs complex SQL queries, using MySQL date functions in the query itself.
(But such queries would not work anymore on 'more or less precise' dates!...)
Conclusion : For 'more or less precise' dates,
I presently discard MySQL date field and use plain VARCHAR(10) field
with aaaa-mm-jj formated data. Simple is beautiful.
Since the data parts are all optional, would it be tedious to store the month, day, and year portions in separate integer fields? Or in a VARCHAR field? 2011-02-00 is not a valid date, and I wouldnt't think mysql or PHP would be excited about it. Test it out with str_to_time and see what kind of results you get, also, did you verify that the sorting worked right in MySQL? If the docs say that 1 through 31 is required, and it is taking 00, you might be relying on what is, in essence, a bug.
Since 2011-02-00 is not a valid date, none of PHP's formatting functions will give you this result. If it handled it at all, I wouldn't be surprised if you got 2001-01-31 if you tried. All the more reason to either store it as a string in the database, or put the month, day, and year in separate integer fields. If you went with the latter route, you could still do sorting on those columns.
I have also encountered this problem. I ended up using the PEAR Date package. Most date classes won't work with optional months or optional days, but the PEAR Date package does. This also means you don't need custom formatting functions and can use the fancy formatting methods provided by the Date package.
I have found this link in a textbook. This states that month and day values can be zero to allow for the possiblity of storing incomplete or unknown data
http://books.google.co.uk/books?id=s_87mv-Eo4AC&pg=PA145&lpg=PA145&dq=mysql+date+of+death+when+month+unknown&source=bl&ots=tcRGz3UDtg&sig=YkwpkAlDtBP1KKTDtqSyZCl63hs&hl=en&ei=Btf5TbL1NIexhAfkveyTAw&sa=X&oi=book_result&ct=result&resnum=8&ved=0CFMQ6AEwBw#v=onepage&q&f=false
If you pull your date in pieces from the database you can get it as if it's 3 fields.
YEAR(dateField) as Year, MONTH(dateField) as Month, DAY(dateField) as DAY
Then pushing those into the corresponding fields in the next bit of PHP will give you the result you're looking for.
$day = 0;
$month = 0;
$year = 2013;
echo $datestring;
$format = "Y";
if($month)
{
$format .= "-m";
if($day)
$format .="-d";
else
$day = 1;
}
else
{
$month = 1;
$day = 1;
}
$datestring = strval($year)."-".strval($month)."-".strval($day);
$date = date($format, strtotime($datestring));
echo $date; // "2013", if $month = 1, "2013-01", if $day and $month = 1, "2013-01-01"