php datetime bug? - php

I am having a problem with php's DateTime functions.
Today is monday 3 december.
Assuming the following code:
$dte = new DateTime(date('Y-m-d H:i:s'));
var_dump($dte->format('Y-W'));
$dte->modify('+4 weeks');
var_dump($dte->format('Y-m-d H:i:s -- Y_W'));
$dte->modify('+1 days');
var_dump($dte->format('Y-m-d H:i:s -- Y_W'));
After four weeks it would be 31st of december. I would suspect to get the last week of the year (52?). But what I get is week 1 of 2012 as you can see in the following output.
string '2012-49' (length=7)
string '2012-12-31 14:48:00 -- 2012_01' (length=30)
string '2013-01-01 14:48:00 -- 2013_01' (length=30)
So my problem is that after the first modification I think I should get:
2012-12-31 14:48:00 -- 2012_52
but instead I get
2012-12-31 14:48:00 -- 2012_01
So why does the week go back to 01 without incrementing the year, and than why does the other line gives me 2013_01 ?
EDIT::
I now see that the week before is week 52, anything to do with leap year?
But then again, how can the week go back to 01 without incrementing the year?

So why does the week go back to 01 without incrementing the year, and than why does the other line gives me 2013_01 ?
I think you're displaying the "year" instead of the "week-year". When you're using week numbers, it's the week-year that's the relevant part; simple "year" is only relevant with respect to month and day.
EDIT: I think you want the o format specifier instead, so try:
var_dump($dte->format('Y-m-d H:i:s -- o_W'));
That should show you 2013_01 for December 31st 2012, as it's in week 1 of week-year 2013.
So basically, I don't think this is a bug in DateTime - it's just a misunderstanding of how "week of year" is meant to be used.

This seems to be no bug. According to the documentation W will return ISO-8601 week number of year, weeks starting on Monday (added in PHP 4.1.0) and because Mon, 31 Dec 2012 15:04:46 +0100 is Monday it will be 1 instead of 52.
Further information on Wikipedia and this nice site.

Related

How to get the starting weekday of a month in a future year using date() in php

I am using a date() format to return the starting weekday of a month. The code I have below is how I am attempting to achieve this. For the current year (2018) this works as normal. For example This month is august and the starting weekday is a Wednesday so it will return a 3 for Wednesday. (It works so far)
As we advance the year to 2019 it starts to get the starting weekday wrong.
For example January 2019 starts on a Tuesday so it should return 2 but returns 1. (one day out)
This error seems to be cumulative so if we go to 2020 then it is 2 days out etc.
I have tried so hard to format this Date() correctly but to no avail. Is this even the correct way to do this?
Code:
$future_month = 5 /*for January 2019*/
$starting_weekday = date('N',mktime(0, 0, 0, date('m', strtotime('+'.$future_month.' months', strtotime(date('Y-m-01')))), 1));
Many Thanks
Cameron
Your code makes this much more complicated than it needs to be.
$dt = new DateTime('first day of +5 months')
$dt->format('N'); // "2"

Quirk with PHP DateTime ISO weeks

I have 3 values:
$timezone = new \DateTimeZone('America/New_York');
$year = 2019;
$week = 52;
I then take these 3 values and run it through a script:
$nowTime = new \DateTime('now', $timezone);
$currTime = clone $nowTime;
$currTime->setISODate($year, $week, 1);
$currTime->setTime(0,0,0);
As you can see, I am setting the current time to be the beginning of Week 52 in 2019.
I am then trying to get information about the next week.
$nextTime = clone $currTime;
$nextTime->modify('+1 week');
$nextWeek = [
'year' => $nextTime->format('Y'),
'week' => $nextTime->format('W'),
];
This script has worked in almost every instance I have found...
Hopever, in Week 52 in 2019, instead of returning the next week as Week 1 in 2020, it returns the next week as Week 1 in 2019... which sends me backwards in time.
How do I fix this? This seems to happen in every year where there are 53 weeks in the year.
You're combining two date formats (Y and W) that don't make sense together. W is the ISO week number, but Y is the calendar year.
The first ISO week of 2020 starts on December 30, 2019, so for that date, W returns 1, but Y still refers to the calendar year 2019.
PHP offers the o date modifier that can be used in place of Y in your code, defined in the manual as:
ISO-8601 week-numbering year. This has the same value as Y, except that if the ISO week number (W) belongs to the previous or next year, that year is used instead. (added in PHP 5.1.0)
So you can change your code to
$nextWeek = [
'year' => $nextTime->format('o'),
'week' => $nextTime->format('W'),
];
and it should work as intended.
There are 2 different ways how weeks can be handled, depending on the task.
1. ISO format:
Weeks start with Monday. Each week's year is the Gregorian year in
which the Thursday falls. The first week of the year, hence, always
contains 4 January. ISO week year numbering therefore slightly
deviates from the Gregorian for some days close to 1 January.
These rules may be hard to handle manually, that's why it's a part of standard PHP, so you can just use 'week' => $nextTime->format('W')
It is indeed possible that a day in 2019 will belong to a week from 2020. And it's easy to prove: when you open a calendar for 2019 / December, you may notice that last week "may share" days from next year. To prevent a problem with overlapping, when the same week belongs to different years but its number is different, e.g. week 52 in a year 2019 is the same as week 1 in a year 2020, they decided to make a set of rules: ISO. These rules are especially useful in accounting to prevent "ambiguous weeks".
2. You only need a "mathematical" week number as a simple counter:
$nextWeek = [
'year' => $nextTime->format('Y'),
'week' => (int)(($nextTime->format('z')) / 7) + 1
];
z formatter is The day of the year (starting from 0) and that's why I am adding "+1" at the end.
Even though it may look as a good approach, it has a drawback: the week #52 in 2019 is the same as week #1 in 2020.

Carbon('last day of October')->modify('+1 month') unexpected result

Can anyone explain to me, why (new Carbon('last day of October'))->modify('+1 month'); returns
Carbon {#326
+"date": "2016-12-01 00:00:00.000000"
+"timezone_type": 3
+"timezone": "Europe/"
}
Same with modify('-1 month'). Could that be that when modifying by a month, Carbon substracts or adds a number of days in the target month? So, when we add "1 month" to the last day of October, Carbon adds 31 days to October 31 and return December 1st...
Seems Carbon library still has some bugs.
You can try this way.
Carbon::create()->endOfMonth()->month(10)->modify('+1 month')
You can check the result at http://phpio.net/tools/carbon
The last day of October evaluates to Oct 31st of the current year.
Oct 31st +1 month is Nov 31st. That's an invalid date. But obviously it should be the same as Nov 30th +1 day, which is Dec 1st.
The problem is that both evaluations are done independently. modify("+1 month") doesn't know that it's supposed to result in the "last day" of that month.
You can't get around this issue by using endOfMonth(), except when it's actually the last date change in the chain. It's basically identical to modify("last day of this month") but also advances the time to 1µs before midnight.
Instead you could concatenate the strings into a single expression, eg. last day of October +1 month.

PHP strtotime for June returns July

I'm stumped as to why the following PHP strtotime function returns '07' as the month number, rather than '06' when $monthToGet = 'June':
$monthToGet = $_GET['mon'];
$monthAsNumber = date('m', strtotime($monthToGet));
From searching, it appears it may be due to default date parameters (in this case the day and year) as I haven't specified them. Would that be the cause?
Any suggestions appreciated!
TL;DR
You are right
echo date("m", strtotime("June"));
-> 07
However, this does work:
echo date("m", strtotime("1. June 2012"));
-> 06
The problem explained
Today is 31. July 2012 and since you provide only a month, the current day and current year are used to create a valid date.
See the documentation:
NOTE
The function expects to be given a string containing an English date format and will try to parse that format into a Unix timestamp (the number of seconds since January 1 1970 00:00:00 UTC), relative to the timestamp given in now, or the current time if now is not supplied.
Alternatives
You could use date_parse_from_format() or strptime() to achieve what you want with a slightly different approach.
(Thanks to johannes_ and johann__ for their input)
Fixed with :
$monthToGet = '1 '. $_GET['mon'];
But I still don't get why, since "m" is a valid date format
Today is 31 Jul. So a strtotime with only "June" is interpreted as 31 June => 1 July.
In fact:
echo date("Y-m-d",strtotime("January")); // 2012-01-31
echo date("Y-m-d",strtotime("February")); // 2012-03-02
of course... only today 31 Jul 2012 :) Tomorrow all will works.
You're lucky because you found this bug just today ;)

PHP date format issue using strtotime()

I am using
$jsdate = date("Y, m, d", strtotime('-1 month', (strtotime($date))));
to convert my dates from
2011-03-28
to
2011, 02, 28
Problem is this is producing unpredictable results. For example today I got
2011-03-28
converted to
2011, 02, 28 // OK
AND
2011-03-29
to
2011, 03, 01 // not OK!
Does anyone know what's wrong here? I wonder if the calculation is inaccurate because of the -1 month.
Is there a way of simply subtracting 1 from m in ...date("Y, m, d", ...?
MORE INFO:
My data needs to be formatted as JavaScript Date Object in which January is 0, Feb is 1, etc. Therefore there is not a need to specifically subtract 1 month but actually subtract 1 from the month integer. At the end, the resulting string is not supposed to be 1 month earlier, but actually the same date, represented using JS Date Object style. I believe #vprimachenko's answer below is a good solution. I apologize if this wasn't clear in my OP.
Thanks!
you might use
$datee = explode('-',$date);
if($datee[1]-- < 0) {
$datee[1]=12;
$datee[0]--;
}
$jsdate = implode(', ',$datee);
The calculation isn't inaccurate, per se. There is no 2/29/2011. If you change your input to 3/29/2012, you'll see that it returns 2/29/2012, because 2012 is a leap year. The same would happen with using something like 7/31/2011. June only has 30 days, so July 31 minus one month would be July 1 (because June 31 doesn't exist).
You could just extract the month, subtract 1, and remake the date, but that will result in attempting to make dates that don't exist.
If you really need the corresponding day of the prior month, you'll probably need to do an if statement of something along the lines of the following to make the day roll back to the last day of February:
$jsdate = date("Y, m, d", strtotime('-1 month', (strtotime($date))));
if($month == '3') {
$jsdate = date("Y, m, d", strtotime('-1 day', (strtotime($jsdate))));
}
You'll also have to account for the rest of the days in March that February doesn't have, as well as leap years, and do something similar for 31-day months that follow 30-day months.
strtotime might work in an unexpected way but it is logical
strtotime('-1 months',strtotime('2011-03-29') // is 2011-02-29
date('Y-m-d','2011-02-29'); //gets converted to the next real date
Here is one kind of fix
http://www.phpreferencebook.com/tips/fixing-strtotime-1-month/

Categories