PHP: determination of the date minus weekends and holidays - php

There is a WordPress and ACF field with a date in the format 2022-04-30. From this date need to calculate 2 other dates, -2 days and +25 days. The problem is that it is necessary to take into account only working days, i.e. check weekends and holidays.
For example, we set the date May 3, 2022, which is Tuesday. From this date, I need to subtract 2 days, i.e. May 1, 2022, but this is Sunday, so we have to return to the first working day before May 1, i.e. Friday April 29, 2022. It's the same with holidays.
At the moment I have this code:
$setDate = get_field('set_date'); // ACF field, set May 3, 2022 (2022-05-03)
$offDate = wp_date('j F Y', strtotime('-2 days', strtotime($setDate)));
echo $offDate; // returns Sunday, May 1, 2022
I found holidays and weekends in json https://github.com/d10xa/holidays-calendar/blob/master/json/consultant2022.json
So I need to compare the given date with the dates from json and if there is a match, then minus one day and check the received date again. If there is a match, again minus one day and so on until no matches are found. Am I thinking correctly and can you tell me how to implement it? I am a very bad programmer, but there is a task)
At the moment I was only able to compare the dates and return the result found/not found. But I can't figure out how to take days off on the basis of this and send it for verification again(
$setDate = '2022-05-01';
$file = file_get_contents('https://raw.githubusercontent.com/d10xa/holidays-calendar/master/json/consultant2022.json', true);
$data = json_decode($file);
$found = array_search($setDate, $data->holidays);
if ($found === False) {
echo 'Not Found';
} else {
echo 'found';
}

The following has been tested on a few dates and I think it works as it should.
/*
JSON file is saved locally to a sub-directory
for the current working script directory.
This is to avoid unneccessary hits to the
remote site.
*/
$format='Y-m-d';
$url='https://raw.githubusercontent.com/d10xa/holidays-calendar/master/json/consultant2022.json';
$setDate = '2022-05-01';
$filepath=sprintf('%s/json/%s', __DIR__, basename( $url ) );
if( !file_exists( $filepath ) ){
file_put_contents( $filepath, file_get_contents( $url ) );
}
# read the file and generate JSON
$json=json_decode( file_get_contents( $filepath ) );
$hols=$json->holidays;
# create the initial DateTime object and find which weekday we are dealing with
# where 1 (for Monday) through 7 (for Sunday)
$obj=new DateTime( $setDate );
$date=$obj->format( $format );
$day=$obj->format('N');
# Is the given date a holiday/weekend?
if( array_search( $date, $hols ) ){
if( $day > 5 ){
# Date is either Sat or Sun... go back to previous working day
$subtract = 2 - ( 7 - $day );
$int=new DateInterval( sprintf('P%sD', $subtract ) );
$obj=new DateTime( $setDate );
$previous=$obj->sub( $int );
}else{
$previous=$obj->sub( new DateInterval('P2D') );
}
# create the future date ( add 25 days )
$int=new DateInterval('P25D');
$obj=new DateTime( $setDate );
$future=$obj->add( $int );
if( array_search( $future->format( $format ), $hols ) ){
# Future date is a holiday... go back to previous working day
$day=$future->format('N');
$subtract = 2 - ( 7 - $day );
$int=new DateInterval( sprintf('P%sD',$subtract ) );
$future=$future->sub( $int );
}
}else{
# Given date is NOT a holiday...
# take a copy of the original DateTime object for generating future date.
$ref=new DateTime( $setDate );
$int=new DateInterval( 'P2D' );
$previous=$obj->sub( $int );
$day=$previous->format('N');
# Is this a holiday?
if( $day > 5 ){
# yes - go back to previous working day
$subtract = 2 - ( 7 - $day );
$int=new DateInterval( sprintf('P%sD',$subtract ) );
$previous=$previous->sub( $int );
}
$int=new DateInterval('P25D');
$future=$ref->add( $int );
$day=$future->format('N');
# Is this a holiday?
if( $day > 5 ){
$subtract = 2 - ( 7 - $day );
$int=new DateInterval( sprintf('P%sD',$subtract ) );
$future=$future->sub( $int );
}
}
printf(
'<pre>
Given date: %s
Previous (-2): %s
Future (+25): %s
</pre>',
$date,
$previous->format( $format ),
$future->format( $format )
);
Which yields:
Given date: 2022-05-01
Previous (-2): 2022-04-29
Future (+25): 2022-05-26

Related

calculate night hours between two date

I want to calculate night hours (21PM to 6AM) between two given dates.
I don't have any ideas
public function biss_hours($start, $end){
$startDate = new \DateTime($start);
$endDate = new \DateTime($end);
$periodInterval = new \DateInterval( "PT1H" );
$period = new \DatePeriod( $startDate, $periodInterval, $endDate );
$count = 0;
foreach($period as $date){
$startofday = clone $date;
$startofday->setTime(5,59);
$endofday = clone $date;
$endofday->setTime(20,59);
if($date > $startofday && $date < $endofday){
$count++;
}
}
return $count;
}
I have this fonction but it's don't work :)
Thx any help
There is no need to loop over a period. As already suggested in the comments, start by calculating the hours of the start and end date since these are the ones that can actually contain less than a full nights hours. Once those are calculated, you can simply get the number of days remaining and multiply them by the amount of hours a night has.
Please note that I only consider hours in my example below, so 22:55 to 24:00 would result in 2 full hours of night time counted. Also it does not check cases where the end date is before the start date or whether inputs are valid. It should get the idea across though:
function getHoursForSingleDay ( $startTime, $endTime, $nightStart, $nightEnd ) {
$numHours = 0;
// if the day starts before night ends
if( $startTime < $nightEnd ) {
// e.g. Night ends at 6a.m. - day starts at 5 a.m. = 1 hour
$numHours += $nightEnd - $startTime;
}
// if the day ends after night starts
if( $endTime > $nightStart ) {
// e.g. day ends at 23 - night starts at 21 = 2 hours
$numHours += $endTime - $nightStart;
}
return $numHours;
}
function biss_hours ( $start, $end, $nightStart = 21, $nightEnd = 6 ) {
$startDate = new \DateTime( $start );
$endDate = new \DateTime( $end );
$startTime = intval( $startDate->format( 'H' ) );
$endTime = intval( $endDate->format( 'H' ) );
// Both dates being the same day is an edge case
if( $startDate->format( 'Y-m-d' ) === $endDate->format( 'Y-m-d' ) ) {
return getHoursForSingleDay( $startTime, $endTime, $nightStart, $nightEnd );
}
// get the hours for bot the start and end date, since they can be less than a full night
$numHours = getHoursForSingleDay( $startTime, 24, $nightStart, $nightEnd );
$numHours += getHoursForSingleDay( 0, $endTime, $nightStart, $nightEnd );
// all remaining days in between can be calculated in a rather simple way
$nightHoursPerDay = $nightEnd + ( 24 - $nightStart );
// -1 because diff returns 1 for two adjacent days, but we treat the first and last day specially
$numDaysBetween = intval( $endDate->diff( $startDate )->format( "%a" ) ) - 1;
$numHours += $numDaysBetween * $nightHoursPerDay;
return $numHours;
}

Display next delivery day based on date time rules in Woocommerce products loop

I have the following code below that creates a line of text under all products on the home, category and related products pages, that says "GREATER CAPE TOWN AREA"…
add_action( 'woocommerce_after_shop_loop_item_title', 'woocommerce_products_loop', 20 );
function woocommerce_products_loop(){
global $product;
echo '<p class="deliveryline">' . __("GREATER CAPE TOWN AREA", "woocommerce") . '</p>';
}
I'd like to have a line of text above this that says "NEXT DELIVERY: " and then uses the following logic:
If it's a weekday BEFORE 12pm, it should say "TOMORROW"
If it's a weekday AFTER 12pm, it should say the name of the business day after tomorrow.
if it's a friday BEFORE 12pm it should say "MONDAY"
if it's a friday AFTER 12pm it should say "TUESDAY"
Basically, we offer next day delivery on weekdays, for orders placed before 12pm.
I have code for something similar that may work..
$now = new Zend_Date();
if (($now->get(Zend_Date::WEEKDAY_DIGIT) % 6 == 0)
|| ($now->isLater('17:00:00', Zend_Date::TIMES))
) {
$now->set(
strtotime('+1 weekday', $now->toString(Zend_Date::TIMESTAMP)),
Zend_Date::TIMESTAMP
);
}
echo $now->toString(Zend_Date::W3C);
I just need help please figuring out the correct maths for what I need (this code is based on same day if before 5pm), and then where do I place it within the original code?
Could someone please help with this complete code snippet? Ideally I want it to look like the attached image.
Desired result:
The following code will display dynamically the delivery day based on your time and dates specifications (You will have to set your shop time zone in the code):
add_action( 'woocommerce_after_shop_loop_item_title', 'woocommerce_products_loop', 20 );
function woocommerce_products_loop(){
global $product;
// Set Your shop time zone (http://php.net/manual/en/timezones.php)
date_default_timezone_set('Europe/Paris');
$is_week_days = in_array( date('w'), array( 1, 2, 3, 4 ) ) ? true : false; // From Monday to Thursday
$is_friday = date('w') == 5 ? true : false; // Friday
$is_week_end = in_array( date('w'), array( 0, 6 ) ) ? true : false; // Weekend days
$end_time = mktime('12', '00', '00', date('m'), date('d'), date('Y')); // 12h00
$now_time = time();
$after_tomorow = date('l', strtotime('+2 days'));
// Displayed day conditions
if( $is_week_days && $now_time < $end_time ) {
$displayed_day = __("TOMORROW", "woocommerce");
} elseif( $is_week_days && $now_time >= $end_time ) {
$displayed_day = strtoupper( date( 'l', strtotime('+2 days') ) );
} elseif( $is_friday && $now_time < $end_time ) {
$displayed_day = __("MONDAY", "woocommerce");
} elseif( ( $is_friday && $now_time >= $end_time ) || $is_week_end ) {
$$displayed_day = __("THUESDAY", "woocommerce");
}
// Dynamic text Output based on date and time
echo '<p class="deliveryline">' . __("NEXT DELIVERY: ", "woocommerce") . $displayed_day .
'<br>' . __("GREATER CAPE TOWN AREA", "woocommerce") . '</p>';
}
Code goes in function.php file of your active child theme (active theme). Tested and works.

Obtaining JSON data with foreach in PHP

A site contains monthly data in a JSON format. This can be queried like so:
http://www.example.com?startdate=1-1-1985&enddate=31-1-1985
I want to be able to run a script that will obtain the specific data I am looking for, starting from today's month, then work backward until null data is returned. Each month's value needs to add up. So far, this is what I've got:
//Build base URL:
$userid=$_GET['userid'];
$startdate=/*Beginning of today's month*/;
$enddate=/*End of today's month*/;
$url='http://www.example.com?userid='.$userid.'&startdate='.$startdate.'&enddate='.$enddate;
//Set JSON variables:
$get=file_get_contents($url);
$json=json_decode($get);
//Set loop variables:
$value=0;
$month=0;
/*For each month from today backwards{
$number=$json->integer;
if($number!=null){
$value=$value+$number;
$month=$month+1;
}else{
break;
}
}
echo $value;
echo $month;
*/
The part I'm having problems with is the beginning of the fourth part. How do I run a loop that starts with the range of today's month, obtain $number, then repeat with the previous month, until it reaches a month that returns null?
You could use the DateTime object and it's associated methods ( specifically sub in this case ) to count backwards by subtracting 1month at a time. An array stores the months/dates and the url is constructed using the variable counter $i
Initially this has a maximum backwards range of 20years ( which I guessed would be more than enough ) but it's easy to change.
$df='Y-m-d';
$userid=$_GET['userid'];
$timezone=new DateTimeZone('Europe/London');
$interval=new DateInterval('P1M');
$ts=date( $df, strtotime( date('Y-m-1') ) );
$tf=date( $df, strtotime( date('Y-m-1'). ' - 20 years') );
$start=new DateTime( $ts, $timezone );
$end=new DateTime( $tf, $timezone );
$dates=array();
$i=0;
while( $start->sub( $interval ) > $end ){
$dates[]=$start->format( $df );
if( $i > 1 ){
$startdate=$dates[ $i - 1 ];
$enddate=$dates[ $i ];
$url='http://www.example.com?userid='.$userid.'&startdate='.$startdate.'&enddate='.$enddate;
echo $url,'<br />';
/* uncomment when happy with mechanism */
/*
$data=file_get_contents( $url );
if( $data ) ) {
$json=json_decode( $data );
#process.....
break;
}
*/
}
$i++;
}
A snippet of the output
http://www.example.com?userid=bobodaclown&startdate=2017-10-01&enddate=2017-09-01
http://www.example.com?userid=bobodaclown&startdate=2017-09-01&enddate=2017-08-01
http://www.example.com?userid=bobodaclown&startdate=2017-08-01&enddate=2017-07-01
http://www.example.com?userid=bobodaclown&startdate=2017-07-01&enddate=2017-06-01
http://www.example.com?userid=bobodaclown&startdate=2017-06-01&enddate=2017-05-01
http://www.example.com?userid=bobodaclown&startdate=2017-05-01&enddate=2017-04-01
http://www.example.com?userid=bobodaclown&startdate=2017-04-01&enddate=2017-03-01
By using strtotime() and mktime() functions you can while loop until you get Null results. The below code will print 5 urls for 5 months. Change while condition accordingly.
// define $i
$i=0;
$userid = '343';//$_GET['userid']; //dont forget to replace with userid
do{
$timestring = strtotime("now -$i month");
//get Month
$month = date('m',$timestring);
//get Year
$year = date('Y',$timestring);
// First date Month and Year
$startdate = date('d-m-Y', mktime(0, 0, 0, $month, 1, $year));
// Last date Month and Year
$enddate = date('d-m-Y', mktime(0, 0, 0, $month+1, 0,$year));
$url='http://www.example.com?userid='.$userid.'&startdate='.$startdate.'&enddate='.$enddate;
// commenting your code
//Set JSON variables:
//$get=file_get_contents($url);
//$json=json_decode($get);
// $number=$json->integer;
echo $url;
echo "\n";
$i++;
}while ($i!=5); // change while condition according to your requirement. while ($number!=null)
Out Put:
http://www.example.com?userid=343&startdate=01-12-2017&enddate=31-12-2017
http://www.example.com?userid=343&startdate=01-11-2017&enddate=30-11-2017
http://www.example.com?userid=343&startdate=01-10-2017&enddate=31-10-2017
http://www.example.com?userid=343&startdate=01-09-2017&enddate=30-09-2017
http://www.example.com?userid=343&startdate=01-08-2017&enddate=31-08-2017

PHP - readable date from negative long timestamp with custom base year

I've seen a number of questions and answers on StackOverflow covering the scenario where timestamps have been based on the Unix Epoch but have been struggling to combine the solutions and modify them into a working solution for the data and custom base year I've been supplied.
<?php
date_default_timezone_set( 'Europe/London' );
function TransformDate( $iTimeStamp ) {
/* Insert transform code here */
$dt = new DateTime( '#' . $iTimeStamp );
return $dt->format( 'Y-m-d H:i:s' );
}
/* These timestamps should be between Sep-2014 and Jan-2015 */
$aTest = array( '-1554460973', '-1554427073', '-1554142396', '-1554139421' );
foreach( $aTest as $i ) echo( $i . ' - ' . TransformDate( $i ) . "\r\n" );
The only information I have to help process these timestamps is that they are based on the year 2013, with 10 ticks per second, and the data was originally from a Microsoft Jet database (so I would imagine similar data storage types to Microsoft Access etc).
I have tried so many combinations of adding or substracting the strtotime result value of 2013-01-01 and 1899-12-30, and multiplying and dividing by 10 that I've simplylost track now. For example:
$iTimeStamp -= strtotime( '2013-01-01' );
$iTimeStamp += strtotime( '1899-12-30' );
$iTimeStamp = round( $iTimeStamp / 10 );
So far though the closest I've got though is dates in 2012, though the December 31st 14:38pm part of the the date could be right:
$iTimeSinceBase = ( $iTimeStamp - strtotime( '2013-01-01' )) / 86400;
$iTimeStamp = strtotime( '2013-01-01' ) + $iTimeSinceBase;
The output for which is:
-1554460973 = 2012-12-31 14:38:22
-1554427073 = 2012-12-31 14:38:22
-1554142396 = 2012-12-31 14:38:26
-1554139421 = 2012-12-31 14:38:26
At this point any assistance is welcome! Thank you.

I want to add 100 years to the displayed wordpress dates

I'm going to create a "future"-blogg (sort of a sci-fi-adventure in blog form) and want to display all dates +100 years. For instance a post published 2012-05-17 should display the date 2112-05-17.
First I thought I could just easily set the date to 2112-05-17, but it seems that wordpress can't handle dates higher than 2049.
So my next idea is to modify how the dates are displayed. I was thinking of modifying get_the_date() in general-template.php, and make it return the later date.
But here my skills are not enough. I don't know anything about how to work with date values in php.
get_the_date() looks like this:
function get_the_date( $d = '' ) {
global $post;
$the_date = '';
if ( '' == $d )
$the_date .= mysql2date(get_option('date_format'), $post->post_date);
else
$the_date .= mysql2date($d, $post->post_date);
return apply_filters('get_the_date', $the_date, $d);
}
Any ideas on how to modify it? So it adds 100 years to the date before returning it?
Any input would be appriciated :)
Looks like you might need to investigate date_modify and also strtotime
http://php.net/manual/en/datetime.modify.php
http://www.php.net/manual/en/function.strtotime.php
http://www.php.net/manual/en/datetime.add.php
Assuming your mysql dates are of the following format: YYYY-MM-DD
function add100yr( $date="2011-03-04" ) {
$timezone=date_timezone_get();
date_default_timezone_set($timezone);
list($year, $month, $day) = split(':', $date);
$timestamp=mktime(0,0,0, $month, $day, $year);
// 100 years, 365.25 days/yr, 24h/day, 60min/h, 60sec/min
$seconds = 100 * 365.25 * 24 * 60 * 60;
$newdate = date("Y-m-d", $timestamp+$seconds );
// $newdate is now formatted YYYY-mm-dd
}
Now you can:
function get_the_date( $d = '' ) {
global $post;
$the_date = '';
if ( '' == $d )
$the_date .= mysql2date(get_option('date_format'), add100yr($post->post_date));
else
$the_date .= mysql2date($d, add100yr($post->post_date));
return apply_filters('get_the_date', $the_date, $d);
}
Try a custom field: http://codex.wordpress.org/Custom_Fields
You will have to enter the +100 year date for each post, but then you're not going to be relying on php or functions to alter the current date.
WordPress provides the filter get_the_date that allows to modify the value before it is handled over to the theme or plugin.
This filter is used everytime get_the_date() is called.
add_filter( 'get_the_date', 'modify_get_the_date', 10, 3 );
function modify_get_the_date( $value, $format, $post ) {
$date = new DateTime( $post->post_date );
$date->modify( "+100 years" );
if ( $format == "" )
$format = get_option( "date_format" );
return( $date->format( $format ) );
}
This function takes the post_date from the post, adds the time and returns it according to the format given to get_the_date() or with the default format configured in the WordPress options.

Categories