SitePoint Sponsor

User Tag List

Page 1 of 2 12 LastLast
Results 1 to 25 of 30

Thread: PHP DateTime()

  1. #1
    Always A Novice bronze trophy
    K. Wolfe's Avatar
    Join Date
    Nov 2003
    Location
    Columbus, OH
    Posts
    2,182
    Mentioned
    66 Post(s)
    Tagged
    2 Thread(s)

    PHP DateTime()

    Im sick, and it's been a long day, so if this is painfully obvious which Im sure it is, I'm sorry.

    This echos nothing:

    Code:
    $cycleDate = new DateTime('2012-12-30');
    //var_dump($cycleDate);
    echo $cycleDate->date;
    This echos both the var dump, annnndd $cycleDate->date.
    Code:
    $cycleDate = new DateTime('2012-12-30');
    var_dump($cycleDate);
    echo $cycleDate->date;
    Why cant I access $cycleDate->date without a var dump being done first?

  2. #2
    Always A Novice bronze trophy
    K. Wolfe's Avatar
    Join Date
    Nov 2003
    Location
    Columbus, OH
    Posts
    2,182
    Mentioned
    66 Post(s)
    Tagged
    2 Thread(s)
    Furthermore Iwould like to express my dissatisfaction for PHPs DateTime classes. I'm finding it most difficult just to manipulate the object to remove 1 month from the date.

  3. #3
    Non-Member
    Join Date
    Oct 2007
    Posts
    363
    Mentioned
    11 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by K. Wolfe View Post
    Im sick, and it's been a long day, so if this is painfully obvious which Im sure it is, I'm sorry.

    This echos nothing:

    Code:
    $cycleDate = new DateTime('2012-12-30');
    //var_dump($cycleDate);
    echo $cycleDate->date;
    This echos both the var dump, annnndd $cycleDate->date.
    Code:
    $cycleDate = new DateTime('2012-12-30');
    var_dump($cycleDate);
    echo $cycleDate->date;
    Why cant I access $cycleDate->date without a var dump being done first?
    Not quite sure why it pumps stuff out when you do the vardump, but I'm pretty sure you're supposed to use: $datetime->format(); to get the date:

    eg:

    PHP Code:
            $cycleDate = new DateTime('2012-12-30');
            echo 
    $cycleDate->format('Y-m-d H:i:s');
    //will echo: 2012-12-30 00:00:00 

  4. #4
    Non-Member
    Join Date
    Oct 2007
    Posts
    363
    Mentioned
    11 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by K. Wolfe View Post
    Furthermore Iwould like to express my dissatisfaction for PHPs DateTime classes. I'm finding it most difficult just to manipulate the object to remove 1 month from the date.
    You need to use the DateInterval class: http://php.net/manual/en/dateinterval.format.php

    Eg:

    PHP Code:
            $cycleDate = new DateTime('2012-12-30');
            echo 
    $cycleDate->format('Y-m-d H:i:s'); //will output 2012-12-30 00:00:00

            
    $monthInterval = new DateInterval('P1M');

            
    $cycleDate->sub($monthInterval);
            echo 
    $cycleDate->format('Y-m-d H:i:s'); //will output 2012-11-30 00:00:00 
    *Edit: the "P" in the DateInterval constructor relates to "period" ie "Period 1 Month".

  5. #5
    Always A Novice bronze trophy
    K. Wolfe's Avatar
    Join Date
    Nov 2003
    Location
    Columbus, OH
    Posts
    2,182
    Mentioned
    66 Post(s)
    Tagged
    2 Thread(s)
    Ya so the problem with this is Feb:

    $cycleDate = new DateTime('2012-02-30');

    While I know 2/30 does not exist, I still need it to return 2012-01-30 as the -1 month value

  6. #6
    Non-Member
    Join Date
    Oct 2007
    Posts
    363
    Mentioned
    11 Post(s)
    Tagged
    0 Thread(s)
    Then I guess the logic you are looking for is slightly different? I'm not sure what your use-case is here? Can you explain what you want a bit more?

    Obviously, there is no 30th Feb, so when you try to set that date, php sets it to the 1st of March, meaning you'd end up with the 1st Feb in your calculations. I don't think it's php that's wrong here though - it's just that your logic is different to actually subtracting a month from another month, for whatever reason.

    Can you explain your use-case in more detail?

  7. #7
    Always A Novice bronze trophy
    K. Wolfe's Avatar
    Join Date
    Nov 2003
    Location
    Columbus, OH
    Posts
    2,182
    Mentioned
    66 Post(s)
    Tagged
    2 Thread(s)
    No PHPs treating it correctly in most cases. I'm sure I'd be way past this if the flu didn't have me down. I'm writing a script for billing cycle statistics. What happened during their previous billing cycle. So on the 28th of every month, I'll be kicking off this calculation for the 28th, 29th, 30th and 31st because not every month has those, however they still need to roll all the way back to what would be exactly 1 month ago according to their bill cycle.

  8. #8
    Hosting Team Leader silver trophybronze trophy
    cpradio's Avatar
    Join Date
    Jun 2002
    Location
    Ohio
    Posts
    5,161
    Mentioned
    152 Post(s)
    Tagged
    0 Thread(s)
    Personally, I like the following approach, it got me the best results:
    PHP Code:
    <?php
        $march 
    = new DateTime('2012-03-31');
        echo 
    $march->format('Y-m-d') . "<br />";
        
    $february $march->modify('last day of previous month');
        echo 
    $february->format('Y-m-d') . "<br />";
        
    $january $february->modify('last day of previous month');
        echo 
    $january->format('Y-m-d') . "<br />";
    ?>
    Whereas, this was just wrong!
    PHP Code:
    <?php
        $oneMonth 
    = new DateInterval('P1M');
        
    $march = new DateTime('2012-03-31');
        echo 
    $march->format('Y-m-d') . "<br />";
        
    $february $march->sub($oneMonth);
        echo 
    $february->format('Y-m-d') . "<br />";
        
    $january $february->sub($oneMonth);
        echo 
    $january->format('Y-m-d') . "<br />";
    ?>

  9. #9
    Non-Member
    Join Date
    Oct 2007
    Posts
    363
    Mentioned
    11 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by cpradio View Post
    Personally, I like the following approach, it got me the best results:
    PHP Code:
    <?php
        $march 
    = new DateTime('2012-03-31');
        echo 
    $march->format('Y-m-d') . "<br />";
        
    $february $march->modify('last day of previous month');
        echo 
    $february->format('Y-m-d') . "<br />";
        
    $january $february->modify('last day of previous month');
        echo 
    $january->format('Y-m-d') . "<br />";
    ?>
    Whereas, this was just wrong!
    PHP Code:
    <?php
        $oneMonth 
    = new DateInterval('P1M');
        
    $march = new DateTime('2012-03-31');
        echo 
    $march->format('Y-m-d') . "<br />";
        
    $february $march->sub($oneMonth);
        echo 
    $february->format('Y-m-d') . "<br />";
        
    $january $february->sub($oneMonth);
        echo 
    $january->format('Y-m-d') . "<br />";
    ?>
    It's crazy that the above works - I had no idea you could type a string like that in the datetime class. It's quite cool I guess...

  10. #10
    Hosting Team Leader silver trophybronze trophy
    cpradio's Avatar
    Join Date
    Jun 2002
    Location
    Ohio
    Posts
    5,161
    Mentioned
    152 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by aaarrrggh View Post
    It's crazy that the above works - I had no idea you could type a string like that in the datetime class. It's quite cool I guess...
    Yeah, I remember first finding that out, and I was like COOL! but why can't I just use a DateInterval, so I tried DateInterval and went *sigh* that's why...

  11. #11
    Hosting Team Leader silver trophybronze trophy
    cpradio's Avatar
    Join Date
    Jun 2002
    Location
    Ohio
    Posts
    5,161
    Mentioned
    152 Post(s)
    Tagged
    0 Thread(s)
    Oh! and before I forget to mention this because it truly is a pain. Both ->sub and ->modify return an instance of DateTime and MODIFY the existing object!

    So if you did this after my above examples:
    PHP Code:
        echo "<br />";
        echo 
    $march->format('Y-m-d') . "<br />";
        echo 
    $february->format('Y-m-d') . "<br />";
        echo 
    $january->format('Y-m-d') . "<br />"
    They will ALL output the same date! Keep that in mind.

  12. #12
    Non-Member
    Join Date
    Oct 2007
    Posts
    363
    Mentioned
    11 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by cpradio View Post
    Oh! and before I forget to mention this because it truly is a pain. Both ->sub and ->modify return an instance of DateTime and MODIFY the existing object!

    So if you did this after my above examples:
    PHP Code:
        echo "<br />";
        echo 
    $march->format('Y-m-d') . "<br />";
        echo 
    $february->format('Y-m-d') . "<br />";
        echo 
    $january->format('Y-m-d') . "<br />"
    They will ALL output the same date! Keep that in mind.
    Someone recommended this to me recently: https://github.com/briannesbitt/Carbon

    Thought it might be worth sharing

  13. #13
    Hosting Team Leader silver trophybronze trophy
    cpradio's Avatar
    Join Date
    Jun 2002
    Location
    Ohio
    Posts
    5,161
    Mentioned
    152 Post(s)
    Tagged
    0 Thread(s)
    Neat! I'll play with that tomorrow to see how well it handles edge cases.

  14. #14
    Hosting Team Leader silver trophybronze trophy
    cpradio's Avatar
    Join Date
    Jun 2002
    Location
    Ohio
    Posts
    5,161
    Mentioned
    152 Post(s)
    Tagged
    0 Thread(s)
    Drats! I had hoped it may have inherited DateTime to fix some of the idiosyncrasies, but alas it doesn't, it just adds additional methods. Not really worth the footprint (in my opinion, but neat nonetheless). This is where I wish PHP took a lesson from the .NET framework. Subtracting one month from 2012-03-31 produces 2012-02-29 appropriately in .NET, PHP instead calculates 2012-02-31 and then pushes that to 2012-03-02, which is annoying.

  15. #15
    Always A Novice bronze trophy
    K. Wolfe's Avatar
    Join Date
    Nov 2003
    Location
    Columbus, OH
    Posts
    2,182
    Mentioned
    66 Post(s)
    Tagged
    2 Thread(s)
    I wound up just passing $day and $month into a function. Thanks for the go at it cp, though it doesn't do what I need it to. (ex. Feb 15th should return Jan 15th.) This was pretty much just me complaining about my inability to work through anything on a flu

  16. #16
    Keeper of the SFL StarLion's Avatar
    Join Date
    Feb 2006
    Location
    Atlanta, GA, USA
    Posts
    3,748
    Mentioned
    72 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by cpradio View Post
    Drats! I had hoped it may have inherited DateTime to fix some of the idiosyncrasies, but alas it doesn't, it just adds additional methods. Not really worth the footprint (in my opinion, but neat nonetheless). This is where I wish PHP took a lesson from the .NET framework. Subtracting one month from 2012-03-31 produces 2012-02-29 appropriately in .NET, PHP instead calculates 2012-02-31 and then pushes that to 2012-03-02, which is annoying.
    Except 2012-02-29 isnt one month back from 2012-03-31... so...
    Never grow up. The instant you do, you lose all ability to imagine great things, for fear of reality crashing in.

  17. #17
    Hosting Team Leader silver trophybronze trophy
    cpradio's Avatar
    Join Date
    Jun 2002
    Location
    Ohio
    Posts
    5,161
    Mentioned
    152 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by StarLion View Post
    Except 2012-02-29 isnt one month back from 2012-03-31... so...
    I disagree wholeheartedly. When you subtract a month, you expect to be in the previous month, not still in the current month. Saying 2012-03-31 minus a month is 2012-03-02 is misleading at best. You should be subtracting the 31 days of March to get to February 29th, as that is the month you are trying to lose. When you are 2012-02-29 and you subtract a month, you should remove the 29 days of February to get to January 31st.

    The 29th of February would be 1 month prior to the end of March. Remember a Month is not a fixed interval, the size of a month differs by month and by year (in case of leap year).

    Quote Originally Posted by K. Wolfe View Post
    I wound up just passing $day and $month into a function. Thanks for the go at it cp, though it doesn't do what I need it to. (ex. Feb 15th should return Jan 15th.) This was pretty much just me complaining about my inability to work through anything on a flu
    You may be able to use $date->modify('15 of previous month'); but I haven't tested that to be sure.

  18. #18
    Keeper of the SFL StarLion's Avatar
    Join Date
    Feb 2006
    Location
    Atlanta, GA, USA
    Posts
    3,748
    Mentioned
    72 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by cpradio View Post
    I disagree wholeheartedly. When you subtract a month, you expect to be in the previous month, not still in the current month. Saying 2012-03-31 minus a month is 2012-03-02 is misleading at best. You should be subtracting the 31 days of March to get to February 29th, as that is the month you are trying to lose. When you are 2012-02-29 and you subtract a month, you should remove the 29 days of February to get to January 31st.
    So by your math, A month before May 30 is April 29th. Uhm... what? (31 days in May)
    Think you meant you need to subtract the number of days in the -preceding- month. Which is how you end up with March 2nd.
    Never grow up. The instant you do, you lose all ability to imagine great things, for fear of reality crashing in.

  19. #19
    Hosting Team Leader silver trophybronze trophy
    cpradio's Avatar
    Join Date
    Jun 2002
    Location
    Ohio
    Posts
    5,161
    Mentioned
    152 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by StarLion View Post
    So by your math, A month before May 30 is April 29th. Uhm... what? (31 days in May)
    Think you meant you need to subtract the number of days in the -preceding- month.
    I see your point, so my basis is flawed to. PHP is currently subtracting the number of days in the preceding month, thus the March 31st minus a month is March 2nd, which is still wrong... So obviously it is a bit more complex than that too.

    This is why I agree with .NET:
    Code c#:
    var march = new DateTime(2012, 03, 31);
    var may = new DateTime(2012, 05, 31);
    Console.WriteLine(may.ToString("yyyy-MM-dd"));
    var april = may.AddMonths(-1);
    Console.WriteLine(april.ToString("yyyy-MM-dd"));
     
    var march = new DateTime(2012, 03, 31);
    Console.WriteLine(march.ToString("yyyy-MM-dd"));
    var february = march.AddMonths(-1);
    Console.WriteLine(february.ToString("yyyy-MM-dd"));
    var january = february.AddMonths(-1);
    Console.WriteLine(january.ToString("yyyy-MM-dd"));
    Console.ReadKey();
    /*
      Output:
      2012-05-31
      2012-04-30
      2012-03-31
      2012-02-29
      2012-01-29 // this one irks me a bit, but it is understandable.
    */

    I have to give it to the MS folks, they really made it nice and maybe I just expect other languages to do it as well.

  20. #20
    Always A Novice bronze trophy
    K. Wolfe's Avatar
    Join Date
    Nov 2003
    Location
    Columbus, OH
    Posts
    2,182
    Mentioned
    66 Post(s)
    Tagged
    2 Thread(s)
    Bah, I'm back to the drawing board on this one. My solution doesn't have handling for new years. So my requirements again:

    $suppliedDate = '2013-02-25'; //yyyy-mm-dd
    $minus1month === '2013-01-25'; //essentially, the day column must never be altered, no matter if any scripting language thinks it makes sense or not.

    ------

    $suppliedDate = '2013-03-29';
    $minus1month === '2013-02-29';

    ----

    $suppliedDate = '2013-01-15';
    $minus1month === '2012-12-15';

    I started to replacing the month area by hand in a string, but then I realized it's not taking care of the year scenario. Should I just write in a case statement, if my starting month is January, subtract from the year?

  21. #21
    Hosting Team Leader silver trophybronze trophy
    cpradio's Avatar
    Join Date
    Jun 2002
    Location
    Ohio
    Posts
    5,161
    Mentioned
    152 Post(s)
    Tagged
    0 Thread(s)
    Like so?
    PHP Code:
        $march = new DateTime('2012-03-15');
        echo 
    $march->format('Y-m-d') . "<br />"// 2012-03-15
        
    $february $march->modify('-1 month');
        echo 
    $february->format('Y-m-d') . "<br />"// 2012-02-15
        
    $january $february->modify('-1 month');
        echo 
    $january->format('Y-m-d') . "<br />"// 2012-01-15
        
    $december $january->modify('-1 month');
        echo 
    $december->format('Y-m-d') . "<br />"// 2011-12-15 
    This will continue to work so long as you don't need to use 29, 30, or 31. If you need to use any of those dates, then you are better off choosing the last day like my prior examples showed.

  22. #22
    Keeper of the SFL StarLion's Avatar
    Join Date
    Feb 2006
    Location
    Atlanta, GA, USA
    Posts
    3,748
    Mentioned
    72 Post(s)
    Tagged
    0 Thread(s)
    except he doesnt want the last day. He wants a string literal that can be a non-existant date. (Why, i'm not sure, but what the hell)

    So lets tweak this slightly.
    PHP Code:
    $day array_pop(explode('-',$suppliedDate));
    $curr = new DateTime('2012-03-15');
    $prev $curr->modify('-1 month');
    echo 
    $prev->format('Y-m-').$day
    EDIT: Except that also wont work. Okay, lets go archaic.

    PHP Code:
    $date explode('-',$suppliedDate);
    $date[1]--;
    if(
    $date[1] == 0) {
     
    $date[0]--;
     
    $date[1] = 12;
    }
    $date implode('-',$date); 
    That'll work as long as you dont try and input year 0.
    Never grow up. The instant you do, you lose all ability to imagine great things, for fear of reality crashing in.

  23. #23
    Hosting Team Leader silver trophybronze trophy
    cpradio's Avatar
    Join Date
    Jun 2002
    Location
    Ohio
    Posts
    5,161
    Mentioned
    152 Post(s)
    Tagged
    0 Thread(s)
    Quote Originally Posted by StarLion View Post
    except he doesnt want the last day. He wants a string literal that can be a non-existant date. (Why, i'm not sure, but what the hell)

    So lets tweak this slightly.
    PHP Code:
    $day array_pop(explode('-',$suppliedDate));
    $curr = new DateTime('2012-03-15');
    $prev $curr->modify('-1 month');
    echo 
    $prev->format('Y-m-').$day
    That probably would work, as so long as you don't initialize your DateTime with 29, 30 or 31 as the day you are fine, you can still output the $day of 29, 30 or 31 appended to the Y and m output. Using 1-28 hard coded for the day for calculation purposes guarantees the -1 month will equate to what he wants and he just has to append the correct day.

    PHP Code:
    $dateparts explode('-',$suppliedDate);
    $day array_pop($dateparts);
    $curr = new DateTime(implode('-'$dateparts) . '-15');
    $prev $curr->modify('-1 month');
    echo 
    $prev->format('Y-m-').$day
    The only issue I see is this is only useful if you can use the dates to retrieve data, and 2012-02-30 or 2012-02-31 will not work in ANY database nor will 2012-04-31, etc (which is why I made the last day argument for days that need be 29, 30, or 31).

  24. #24
    Keeper of the SFL StarLion's Avatar
    Join Date
    Feb 2006
    Location
    Atlanta, GA, USA
    Posts
    3,748
    Mentioned
    72 Post(s)
    Tagged
    0 Thread(s)
    Yeah, you can never insert those dates into a type-checked field, but if you're doing simple comparisons (SELECT X where date BETWEEN Y and Z) it... should work....
    Never grow up. The instant you do, you lose all ability to imagine great things, for fear of reality crashing in.

  25. #25
    SitePoint Wizard bronze trophy Jeff Mott's Avatar
    Join Date
    Jul 2009
    Posts
    1,277
    Mentioned
    18 Post(s)
    Tagged
    0 Thread(s)
    I think a lot of these troubles stem from a flaw in the requirements. Most notably:

    "they still need to roll all the way back to what would be exactly 1 month ago."

    This is flawed because, of course, there's no such thing as exactly 1 month, and trying to pretend otherwise is forcing you to attempt computing from or to some invalid dates.

    Since this is supposed to match a billing cycle, it's probably worthwhile to ask your billing department how they handle those edge cases. Ask them if Jan 31 +1 month should be Feb 28 or Mar 3? Or if Feb 28 -1 month should be Jan 28 or Jan 31? Etc.
    "First make it work. Then make it better."


Bookmarks

Posting Permissions

  • You may not post new threads
  • You may not post replies
  • You may not post attachments
  • You may not edit your posts
  •