Month conversion to words - datetime

I have dates stored in array for completion status of projects like below
The first two letters are months and other four letters are years
052012(mmyyyy)
$arrDates = array('052012', '042013', '082013', '122013', '022014');
I want this array To get converted to wordings as below
$arrDates = array('Completed', 'Next Six Months', 'Next Year', 'Next Year', 'After 2 Years');
I used a for loop like below for checking completed as below
for($i=0;$i<count($arrDates);$i++)
{
if((date('m') > substr($arrDates[$i], 0,2)) && (date('y') == substr($arrDates[$i], 2,6)))
$strStatus = ' Completed';
}
and I messed up in finding for next year and other two.
Could some one help me in fixing this?

I think you can use DateTime and dateInterval for this :
$currentDate = DateTime::createFromFormat('mY',$arrDates[0]);
$interval = $currentDate->diff(new DateTime('now'),true);
if($interval->invert){
echo 'Completed';
else if($interval->y >= 2 ){
echo 'After 2 Years';
}
else if($interval->y == 1){
echo 'Next Year';
}
else if($interval->m > 6){
echo 'what you want here';
}
else {
echo 'Next six months';
}

I think artragis was on the right track with his answer (+1 for that), but the whole thing should be encapsulated in a function for re-use.
/**
* #param string $expectedDate
* #return string Text summary of time to completion
*/
function completionTimeInWords($expectedDate)
{
$now = new DateTime();
$date = new DateTime();
$date->setTimestamp(strtotime('+1 year'));
$oneYear = $date->diff($now)->days;
$sixMonths = round($oneYear/2);
$date->setTimestamp(strtotime('+2 years'));
$twoYears = $date->diff($now)->days;
$date = DateTime::createFromFormat('mY', $expectedDate);
$interval = $now->diff($date);
$completed = (bool)$interval->invert;
$days = $interval->days;
if($completed){
return 'Completed';
} elseif($days <= $sixMonths){
return 'Next Six Months';
} elseif($days > $sixMonths && $days <= $oneYear){
return 'Within a year';
} elseif($days > $oneYear && $days <= $twoYears){
return 'Next Year';
} elseif($days > $twoYears){
return 'After Two Years';
}
}
I tested this with the following code:-
$date = new DateTime();
$date->setTimestamp(strtotime('two weeks ago'));
$arrDates[] = $date->format('mY');
$date->setTimestamp(strtotime('+ 5 months 27 days'));
$arrDates[] = $date->format('mY');
$date->setTimestamp(strtotime('+ 6 months 4 days'));
$arrDates[] = $date->format('mY');
$date->setTimestamp(strtotime('+ 12 months 4 days'));
$arrDates[] = $date->format('mY');
$date->setTimestamp(strtotime('+ 24 months 4 days'));
$arrDates[] = $date->format('mY');
foreach($arrDates as $date){
var_dump(completionTimeInWords($date));
}
and got the following output:-
string(9) "Completed"
string(15) "Next Six Months"
string(13) "Within a year"
string(9) "Next Year"
string(15) "After Two Years"
You may need to do some more testing with edge cases as these can be problematic. For example, most of us would agree that 6 months is 183 days, but PHP may disagree on occasion depending on how many days are in each month between now and 6 months hence (if that makes sense). That is why I have divided a year by 2 to get 6 months, rather than using strtotime().

Related

Custom price format based on decimal and zeros in Woocommerce

I am struggling to find a solution to my price format issue.
If the price in cart adds up to $1 then it should show $1.00 Now, this works if I change the decimal in WC settings to "2".
However, if wanted it to show as 0.065 and 0.010 So I added a "3" in the WC decimal settings. So, how can I get the price to look like this:
$0.065
$0.01 (and NOT this 0.010)
I just want to remove the trailing zero if it is a zero.
So I have tried a few things:
add_filter( 'woocommerce_price_trim_zeros', 'wc_hide_trailing_zeros', 10, 1 );
function wc_hide_trailing_zeros( $trim ) {
return true;
}
This removes ALL zeros.
Then I tried a function that supposed to remove the last item in a str.
function rstrtrim($str, $remove=null)
{
$str = (string)$str;
$remove = (string)$remove;
if(empty($remove))
{
return rtrim($str);
}
$len = strlen($remove);
$offset = strlen($str)-$len;
while($offset > 0 && $offset == strpos($str, $remove, $offset))
{
$str = substr($str, 0, $offset);
$offset = strlen($str)-$len;
}
return rtrim($str);
}
//Remove last zero on a 3 decimal setting
echo rstrtrim((string)$number_format, '0');
But I think it's not working because it is reading it as a string and not an integer, dunno.
So, all I want to do is set my decimal settings in WC to "3" and remove the "last" integer IF it is a zero...
so this:
$1.000
$0.065
$0.010
Would be this:
$1.00
$0.065
$0.01
Ok, I figured it out!
add_filter( 'formatted_woocommerce_price', 'wc_custom_price_format', 10, 5 );
function wc_custom_price_format( $number_format, $price, $decimals, $decimal_separator, $thousand_separator){
$lastnum = $number_format[strlen($number_format)-1];
if ($lastnum == 0):
return substr($number_format, 0, -1);
else:
return $number_format;
endif;
}

PHP: Calculating Monthly/Yearly Billing Dates - Same day next month

I've been looking for a way to accurately calculate the next/future billing date in a way that a month will not be skipped. For example for Jun 31 I want the next billing date to be Feb 28 (or 29 when applicable).
I found this great solution.
Here is an update of that code to support more than 1 month in the future.
Hope that helps.
Code:
function calculateBillingDate($startDate, $numberOfCycles = 1) {
$currentMonth = date('n', $startDate);
$nextMonth = (($currentMonth + $numberOfCycles) % 12);
if ($nextMonth === 0) {
$nextMonth = 12;
}
$targetDate = new \DateTime();
$targetDate->setTimestamp($startDate);
$targetDate->add(new \DateInterval('P' . $numberOfCycles . 'M'));
while ((int)$targetDate->format('m') !== $nextMonth) {
$targetDate->sub(new \DateInterval('P1D'));
}
return $targetDate;
}

Symfony 2 Bizarre behavior in the controller

Symfony 2.3
I have two fields on the form with dates: the date_from and date_to.
The controller after submit performs its own validation. After automatic validatin is OK make my own validation of dates. Before adding the code below the form and submit displays after validation:
date_from 2015-10-01 and date_to 2015-10-02
and after adding the code form displays before submit:
date_from 2015-10-01 and date_to 2015-10-02
after submit:
date_from 2015-10-02 and date_to 2015-10-01
Dates are switched. So it writes to the database.
$date_from = $entity->getDatefrom();
$date_to = $entity->getDateto();
$workdays = 0;
for ( $i = $date_from; $i <= $date_to; $i->setTimestamp(strtotime($i->format('Y-m-d') . " +1 day") ) )
{
if ( !in_array($i, $freedays))
{
if ( date('N', strtotime($i->format('Y-m-d') ) ) < 6 )
{
$workdays++ ;
}
}
}
When i remove this code dates are not switched. But ofcourse i don't have count of working days.
You need to dereference the DateTime Object $date_from using clone before assigning it to $i.
http://php.net/manual/en/language.oop5.references.php
$date_from = $entity->getDatefrom();
$date_to = $entity->getDateto();
$workdays = 0;
for ( $i = clone $date_from; $i <= $date_to; $i->setTimestamp(strtotime($i->format('Y-m-d') . " +1 day") ) )
{
if ( !in_array($i, $freedays))
{
if ( date('N', strtotime($i->format('Y-m-d') ) ) < 6 )
{
$workdays++ ;
}
}
}
PHP doesn't copy objects when assigning them to a new variable it just creates a reference.
$i = $date_from
In this for loop it updates the $i for each iteration
$i->setTimestamp(strtotime($i->format('Y-m-d') . " +1 day") )
Because it's only a reference its this same as
$date_from->setTimestamp(strtotime($i->format('Y-m-d') . " +1 day") )
// OR even
$entity->getDatefrom()->setTimestamp(strtotime($i->format('Y-m-d') . " +1 day") )
Using clone make a copy of the original so $i->setTimestamp(...) won't actually apply to $date_from

Conditional display of start and end dates (PHP/Wordpress)

(As you can tell from this post I am not a programmer, so please help me to see if this is even possible... )
I want to improve the Start / End date display for events in Events Manager (plugin for WordPress)
At the moment the the end times will only show up if different then the start date (good!) but will be in the format:
1 January 2014 - 31 January 2014.
I would like it to display as: 1 - 31 January 2014.
That isn't too hard, but it becomes a little more complicated when events wrap or extend past a month, or a year.
Here are the different cases I am trying to achieve:
Start date only:
1 January 2014
Start and end date in same month
1 – 12 January 2014
Start and end date in different month but same year.
28 January – 3 February 2014
Start and end date in different months and different years.
31 December 2014 – 3 January 2015
Can anyone tell me
a) if this is at all possible to do with a function in the WordPress functions file?
and
b) point me in the right direction on how to do the conditioning?
I don't even know what to google for, so even that would be a help. (I am not expecting a fully working code example, although you are more then welcome to include that if you want!)
Update: Here is an example of a function that does some of this, but only in the style of the last case in my list above.
add_filter('em_event_output_placeholder','my_em_placeholder_mod_eventdates',1,3);
function my_em_placeholder_mod_eventdates($replace, $EM_Event, $result){
if ( $result == '#_EVENTDATES' ) {
if( $EM_Event->event_start_date != $EM_Event->event_end_date){
$replace = date_i18n('d M Y', $EM_Event->start).' - '. date_i18n('d M Y', $EM_Event->end);
}else{
$replace = date_i18n('d M Y', $EM_Event->start);
}
}
return $replace;
}
Update 2, with working code:
This code works for me at least.
add_filter('em_event_output_placeholder','my_em_placeholder_mod_eventdates',1,3);
function my_em_placeholder_mod_eventdates($replace, $EM_Event, $result){
if ( $result == '#_EVENTDATES' ) :
$sd = $EM_Event->start;
$ed = $EM_Event->end;
if (!empty ($ed) && $sd != $ed ) {
//Event has an end date and end date is different than start date
if (date('Y', $sd) == date('Y', $ed)) {
// Start and end are in same year
if (date('n', $sd) == date('n', $ed)) {
//Start and end are in the same month
$replace = date_i18n('j', $sd).'-'.date_i18n('j F Y', $ed);
if (date('j', $sd) == date('j', $ed)) {
//Start and end are on the same day
$replace = date_i18n('j F Y', $sd);
}
} else {
//Start and end are in different months
$replace = date_i18n('j F', $sd).' - '.date_i18n('j F Y', $ed);
}
} else {
//Start and end are in different years
$replace = date_i18n('j F Y', $sd).' - '. date_i18n('j F Y', $ed);
}
} else {
// No end date, or start and end date are the same
$replace = date_i18n('j F Y', $sd);
}
endif;
return $replace;
}
Try this:
add_filter('em_event_output_placeholder','my_em_placeholder_mod_eventdates',1,3);
function my_em_placeholder_mod_eventdates($replace, $EM_Event, $result){
if ( $result == '#_EVENTDATES' ) :
$sd = $EM_Event->event_start_date;
$ed = $EM_Event->event_end_date;
if (!empty ($ed) && $sd != $ed ) {
//Event has an end date and end date is different than start date
if (date('Y', $sd) == date('Y'. $ed) {
// Start and end are in same year
if (date('n', $sd) == date('n', $ed) {
//Start and end are in the same month
$replace = date_i18n('j', $sd).'-'.date_i18n('j F Y', $ed);
} else {
//Start and end are in different months
$replace = date_i18n('j F', $sd).' - '.date_i18n('j F Y', $ed);
}
} else {
//Start and end are in different years
$replace = date_i18n('j F Y', $sd).' - '. date_i18n('j F Y', $ed);
}
} else {
// No end date, or start and end date are the same
$replace = date_i18n('j F Y', $sd);
}
endif;
return $replace;
}

How to Calculate Two Different Dates like 2 years, 5 months

I want to calculate the difference between two dates and want to convert it like 2 years, 5 months or only 3 months, or 2 days according to the difference considering all months are 30 days...
For example;
From and including: Mar 12, 2009
To, but not including : Nov 26, 2011
The output must be : 2 years, 8 months, 14 days excluding the end date.
Another example;
Start: Jan 26, 2010
End: Feb 15, 2010
Output: 20 days from the start date to the end date, but not including the end date
I can calculate the difference as month, day or hour with Datediff but the question is how to convert it to years, months and dates. It's quite complicated actually as we don't know how many days there are between two months (30,31 maybe 28 days)
I use this Classic ASP code to convert the difference but there are lot's of disadvantages.
Function Convert_Date_to_Text(tarih1,tarih2,useDates)
if (tarih1<>"" AND tarih2<>"") then
if Tarih_Araligi_Belirle(tarih1,tarih2,"day")>0 then
Date1_Year = Year(tarih1)
Date1_Month = Month(tarih1)
Date1_Day = Day(tarih1)
Date2_Year = Year(tarih2)
Date2_Month = Month(tarih2)
Date2_Day = Day(tarih2)
If (Date1_Month = 12) and (Date1_Day = 31) and
(Date2_Month = 1) and (Date2_Day = 1) Then
NoOfyears = Date2_Year - Date1_Year - 1
NoOfmonths = 0
NoOfdays = 1
Else
NoOfyears = Date2_Year - Date1_Year
NoOfmonths = Date2_Month - Date1_Month
NoOfdays = Date2_Day - Date1_Day
End If
If NoOfyears = 1 Then
FormatString = "1 year "
Else If NoOfyears <= 0 then
FormatString = ""
Else
FormatString = CStr(NoOfyears) & " years "
End If:End If
If NoOfmonths = 1 Then
FormatString = FormatString & "1 month"
Else If NoOfmonths <= 0 then
FormatString = FormatString
Else
FormatString = FormatString & CStr(NoOfmonths) & " months "
End If:End If
if useDates=1 then
If NoOfdays = 1 Then
FormatString = FormatString & "1 day"
Else If NoOfdays <= 0 Then
FormatString = FormatString
Else
FormatString = FormatString & CStr(NoOfdays) & " days"
End If:End If
end if
end if
end if
Convert_Date_to_Text = FormatString
End Function
This web site calculates the difference perfectly. TimeAndDate.Com
Note: I'm using Classic ASP for several reasons (Company limitations). Sorry for this but I need an ASP function. It looks like TimeSpan doesn't exist in ASP :(
Kind Regards
If you can convert the input strings to DateTime variables, you can try something like this:
DateTime starTime = //something;
DateTime endTime = //something;
TimeSpan oneDay = new TimeSpan(1, 0, 0, 0); //creates a timespan of 1 day
TimeSpan deltaTime = (endTime - startTime) - oneDay;
I would asume asp has the DateTime and TimeSpan variable types.
Here's a function I have used in the past. If you test it, I think you'll find it accurate. Here's where I got it from.
Function YearsMonthsDays(Date1 As Date, Date2 As Date, Optional ShowAll As _
Boolean = False, Optional Grammar As Boolean = True)
' This function returns a string "X years, Y months, Z days" showing the time
' between two dates. This function may be used in any VBA or VB project
' Date1 and Date2 must either be dates, or strings that can be implicitly
' converted to dates. If these arguments have time portions, the time portions
' are ignored. If Date1 > Date2 (after ignoring time portions), the function
' returns an empty string
' ShowAll indicates whether all portions of the string "X years, Y months, Z days"
' are included in the output. If ShowAll = True, all portions of the string are
' always included. If ShowAll = False, then if the year portion is zero the year
' part of the string is omitted, and if the year portion and month portion are both
' zero, than both year and month portions are omitted. The day portion is always
' included, and if at least one year has passed then the month portion is always
' included
' Grammar indicates whether to test years/months/days for singular or plural
' By definition, a "full month" means that the day number in Date2 is >= the day
' number in Date1, or Date1 and Date2 occur on the last days of their respective
' months. A "full year" means that 12 "full months" have passed.
' In Excel, this function is an alternative to the little-known DATEDIF. DATEDIF
' usually works well, but can create strange results when a date is at month end.
' Thus, this formula:
' =DATEDIF(A1,B1,"y") & " years, " & DATEDIF(A1,B1,"ym") & " months, " &
' DATEDIF(A1,B1,"md") & " days"
' will return "0 years, 1 months, -2 days" for 31-Jan-2006 and 1-Mar-2006.
' This function will return "0 years, 1 month, 1 day"
Dim TestYear As Long, TestMonth As Long, TestDay As Long
Dim TargetDate As Date, Last1 As Date, Last2 As Date
' Strip time portions
Date1 = Int(Date1)
Date2 = Int(Date2)
' Test for invalid dates
If Date1 > Date2 Then
YearsMonthsDays = ""
Exit Function
End If
' Test for whether the calendar year is the same
If Year(Date2) > Year(Date1) Then
' Different calendar year.
' Test to see if calendar month is the same. If it is, we have to look at the
' day to see if a full year has passed
If Month(Date2) = Month(Date1) Then
If Day(Date2) >= Day(Date1) Then
TestYear = DateDiff("yyyy", Date1, Date2)
Else
TestYear = DateDiff("yyyy", Date1, Date2) - 1
End If
' In this case, a full year has definitely passed
ElseIf Month(Date2) > Month(Date1) Then
TestYear = DateDiff("yyyy", Date1, Date2)
' A full year has not passed
Else
TestYear = DateDiff("yyyy", Date1, Date2) - 1
End If
' Calendar year is the same, so a full year has not passed
Else
TestYear = 0
End If
' Test to see how many full months have passed, in excess of the number of full
' years
TestMonth = (DateDiff("m", DateSerial(Year(Date1), Month(Date1), 1), _
DateSerial(Year(Date2), Month(Date2), 1)) + IIf(Day(Date2) >= _
Day(Date1), 0, -1)) Mod 12
' See how many days have passed, in excess of the number of full months. If the day
' number for Date2 is >= that for Date1, it's simple
If Day(Date2) >= Day(Date1) Then
TestDay = Day(Date2) - Day(Date1)
' If not, we have to test for end of the month
Else
Last1 = DateSerial(Year(Date2), Month(Date2), 0)
Last2 = DateSerial(Year(Date2), Month(Date2) + 1, 0)
TargetDate = DateSerial(Year(Date2), Month(Date2) - 1, Day(Date1))
If Last2 = Date2 Then
If TestMonth = 11 Then
TestMonth = 0
TestYear = TestYear + 1
Else
TestMonth = TestMonth + 1
End If
Else
TestDay = DateDiff("d", IIf(TargetDate > Last1, Last1, TargetDate), Date2)
End If
End If
If ShowAll Or TestYear >= 1 Then
YearsMonthsDays = TestYear & IIf(TestYear = 1 And Grammar, " year, ", _
" years, ") & TestMonth & IIf(TestMonth = 1 And Grammar, " month, ", _
" months, ") & TestDay & IIf(TestDay = 1 And Grammar, " day", " days")
Else
If TestMonth >= 1 Then
YearsMonthsDays = TestMonth & IIf(TestMonth = 1 And Grammar, " month, ", _
" months, ") & TestDay & IIf(TestDay = 1 And Grammar, " day", " days")
Else
YearsMonthsDays = TestDay & IIf(TestDay = 1 And Grammar, " day", " days")
End If
End If
End Function
How about this? (no TimeSpan but not sure if classic asp compatible)
DateTime dateTime1 = new DateTime(2003,2,2);
DateTime dateTime2 = new DateTime(2001,1,1);
int daysDiff = dateTime1.Day - dateTime2.Day;
int monthsDiff = dateTime1.Month - dateTime2.Month;
int yearsDiff = dateTime1.Year - dateTime2.Year;
if (daysDiff < 0)
{
daysDiff += DateTime.DaysInMonth(dateTime1.Year, dateTime1.Month);
monthsDiff--;
}
if (monthsDiff < 0)
{
monthsDiff += 12;
yearsDiff--;
}
Console.WriteLine(daysDiff);
Console.WriteLine(monthsDiff);
Console.WriteLine(yearsDiff);
You can subtract DateTime objects to get a TimeSpan object:
DateTime startDate = GetStartDate();
DateTime endDate = GetEndDate();
TimeSpan duration = endDate - startDate;
This article includes a DateDiff class:
// ----------------------------------------------------------------------
public void DateDiffSample()
{
DateTime date1 = new DateTime( 2009, 11, 8, 7, 13, 59 );
Console.WriteLine( "Date1: {0}", date1 );
// > Date1: 08.11.2009 07:13:59
DateTime date2 = new DateTime( 2011, 3, 20, 19, 55, 28 );
Console.WriteLine( "Date2: {0}", date2 );
// > Date2: 20.03.2011 19:55:28
DateDiff dateDiff = new DateDiff( date1, date2 );
// differences
Console.WriteLine( "DateDiff.Years: {0}", dateDiff.Years );
// > DateDiff.Years: 1
Console.WriteLine( "DateDiff.Quarters: {0}", dateDiff.Quarters );
// > DateDiff.Quarters: 5
Console.WriteLine( "DateDiff.Months: {0}", dateDiff.Months );
// > DateDiff.Months: 16
Console.WriteLine( "DateDiff.Weeks: {0}", dateDiff.Weeks );
// > DateDiff.Weeks: 70
Console.WriteLine( "DateDiff.Days: {0}", dateDiff.Days );
// > DateDiff.Days: 497
Console.WriteLine( "DateDiff.Weekdays: {0}", dateDiff.Weekdays );
// > DateDiff.Weekdays: 71
Console.WriteLine( "DateDiff.Hours: {0}", dateDiff.Hours );
// > DateDiff.Hours: 11940
Console.WriteLine( "DateDiff.Minutes: {0}", dateDiff.Minutes );
// > DateDiff.Minutes: 716441
Console.WriteLine( "DateDiff.Seconds: {0}", dateDiff.Seconds );
// > DateDiff.Seconds: 42986489
// elapsed
Console.WriteLine( "DateDiff.ElapsedYears: {0}", dateDiff.ElapsedYears );
// > DateDiff.ElapsedYears: 1
Console.WriteLine( "DateDiff.ElapsedMonths: {0}", dateDiff.ElapsedMonths );
// > DateDiff.ElapsedMonths: 4
Console.WriteLine( "DateDiff.ElapsedDays: {0}", dateDiff.ElapsedDays );
// > DateDiff.ElapsedDays: 12
Console.WriteLine( "DateDiff.ElapsedHours: {0}", dateDiff.ElapsedHours );
// > DateDiff.ElapsedHours: 12
Console.WriteLine( "DateDiff.ElapsedMinutes: {0}", dateDiff.ElapsedMinutes );
// > DateDiff.ElapsedMinutes: 41
Console.WriteLine( "DateDiff.ElapsedSeconds: {0}", dateDiff.ElapsedSeconds );
// > DateDiff.ElapsedSeconds: 29
// description
Console.WriteLine( "DateDiff.GetDescription(1): {0}", dateDiff.GetDescription( 1 ) );
// > DateDiff.GetDescription(1): 1 Year
Console.WriteLine( "DateDiff.GetDescription(2): {0}", dateDiff.GetDescription( 2 ) );
// > DateDiff.GetDescription(2): 1 Year 4 Months
Console.WriteLine( "DateDiff.GetDescription(3): {0}", dateDiff.GetDescription( 3 ) );
// > DateDiff.GetDescription(3): 1 Year 4 Months 12 Days
Console.WriteLine( "DateDiff.GetDescription(4): {0}", dateDiff.GetDescription( 4 ) );
// > DateDiff.GetDescription(4): 1 Year 4 Months 12 Days 12 Hours
Console.WriteLine( "DateDiff.GetDescription(5): {0}", dateDiff.GetDescription( 5 ) );
// > DateDiff.GetDescription(5): 1 Year 4 Months 12 Days 12 Hours 41 Mins
Console.WriteLine( "DateDiff.GetDescription(6): {0}", dateDiff.GetDescription( 6 ) );
// > DateDiff.GetDescription(6): 1 Year 4 Months 12 Days 12 Hours 41 Mins 29 Secs
} // DateDiffSample
Dim intYears
Dim intMonths
Dim intDays
Dim strDate1
Dim strDate2
Dim strAnswer
strDate1 = "01/26/2010"
strDate2 = "02/15/2010"
intYears = DateDiff("yyyy",strDate1,strDate2)
intMonths = DateDiff("m",strDate1,strDate2)
intDays = DateDiff("d",strDate1,strDate2)
strAnswer = ""
if intYears > 0 then
strAnswer = strAnswer & CStr(intYears) & "years "
end if
if intMonths > 0 then
strAnswer = strAnswer & CStr(intMonths) & "months"
end if
if intDays > 0 then
strAnswer = strAnswer & CStr(intDays) & "days"
end if
Response.Write("The difference between these two dates is " & strAnswer)

Resources