I have this date / time value (with timezone):
2019-10-22T17:00:00+02:00
Now I would like to check, which weekday this date is (for example: Monday)
and if this date has a difference of 1h between 16:00 o'clock and the date time.
How can I check the two factors as best practice ?
You may use DateTimeImmutable's constructor to parse the date string, DateTime#format to format it / retrieve the week day, and DateTime#diff to fetch the difference:
$date_string = '2019-10-22T17:00:00+02:00';
$date = new \DateTimeImmutable($date_string);
$date_at_16 = $date->setTime(16, 0);
echo $date->format('l'), PHP_EOL;
echo $date->diff($date_at_16)->h;
Demo: https://3v4l.org/R7e9n
Note that:
I've used DateTimeImmutable which is just like DateTime except it cannot be modified, so setTime doesn't also modify the initial date,
you should catch its constructor's potential thrown exception (if the format is invalid),
it should be better to use 'N' to retrieve the day of the week, as it's numerical and therefore more appropriate to store/compare (I've used 'l' for the purpose of this little demo, to get the full name),
if you need to know if it's 1 hour prior to or following 16 o'clock, you may check the DateInterval#invert flag (the date interval is what DateTime#diff returns).
Related
Given an arbitrary timestamp (e.g. 2019-02-26 10:30:00) I would like to find the next occurrence of an arbitrary time.
For example, the next occurrence of 12:00:00 will be 2019-02-26 12:00:00 but the next occurrence of 09:00:00 will be the next day at 2019-02-27 09:00:00. The results could be Carbon or Datetime objects. The test time will just be a string as shown.
Is there a way to calculate this in native PHP or PHP Carbon without conditionally boxing in time periods. An obvious way would be to see if the time being tested is past the check time for today, and if it is, taking the result as the check time plus 24 hours (the next day). That feels to me like too much chopping and joining of dates and times, so is there a way to calculate it by considering time to be a simple linear line?
All times will be in a single timezone, with DST. Note: the arbitrary datetimes and check times will stay clear of DST changeovers i.e. 01:00 to 02:00 so hopefully they will not be an issue to take into account.
Short answer is no for PHP (partial answer, I'm no specialist of Carbon but from quick look it's also no, but you can create a macro from following code).
However, with a ternary condition the one-liner is simple enough IMHO (replace the second DateTime($str) with DateTime() if you want to compare with current date and time, and change the >= by > if you want next day when time compared is exactly the same):
$str = '2019-02-26 10:30:00';
$date1 = ( ($a = (new DateTime($str))->setTime(12,00)) >= (new DateTime($str)) ) ? $a : $a->modify('+1 day');
$date2 = ( ($a = (new DateTime($str))->setTime(9,00)) >= (new DateTime($str)) ) ? $a : $a->modify('+1 day');
echo $date1->format('Y-m-d H:i:s'); //2019-02-26 12:00:00
echo $date2->format('Y-m-d H:i:s'); //2019-02-27 09:00:00
quick note: what you gave us is not a timestamp, but a formatted date.
Here is what I am using now through Carbon, which appears to give me the correct results:
$dateTime = Carbon::parse('2019-03-30 17:34:50', 'Europe/London');
$testTime = '16:00:00';
list ($hour, $minute, $second) = explode(':', $testTime);
$nextTimeOccurrence = $dateTime
->copy() // Carbon 1 only
->hour($hour)->minute($minute)->second($second);
if ($dateTime->gt($nextTimeOccurrence)) {
$nextTimeOccurrence = $nextTimeOccurrence->addDay();
}
// $nextTimeOccurrence is the next occurrence of $testTime after $dateTime
The splitting of the time seems clumsy, but might be the best way? The approach is:
Create a timestamp with the test time on the same day as the timestamp I'm checking. This will be the timestamp I am looking for.
If the timestamp I'm checking is after the timestamp created in the previous step, then add a day to it.
I've tested this around DST, and happily Carbon/Datetime keeps the same time when adding a day over a DST period, where a day there would be 25 hours or 23 hours, depending on which way it goes.
I still think there is a more "linear time" way to do this, but this seems simple and robust. Thanks go to #michael-stokoe here at the office for my lead on this.
I'm using Carbon to manipulate dates I retrieved from my MySQL database. I have dates like the following:
2017-07-19 00:00:00
2017-06-26 15:27:57
As you can see, the first is the start of a day. When displaying dates like that, I would like to omit the time part. I know I can use a different format for each one. For example:
F d Y for dates without time.
F d Y g:ia for dates with time.
What I couldn't accomplish is a simple way to check if a date has a time part to apply one format or the other. Must I use individual getters to check the hour, minute and second?
If you just want to check if it's the start of the day, then it's fairly easy to check with Carbon's startOfDay() modifier and a comparison:
$date = Carbon::now(); // or whatever you're using to set it
$start = $date->copy()->startOfDay();
if($date->eq($start)) {
// do your formatting here
}
In the meanwhile its even easier. Use the is....() methods (see carbon comparison) like this:
$date = Carbon::now();
if($date->isStartOfDay()) { // check if hour is 00:00:00
// whatever
}
You can use the timestamp of the Carbon object minus the timestamp of "today" (which is yyyy-mm-dd 0000:00:00) and it will give you the number of seconds that passed from 00:00 to that date:
$secondsPassed = $carbonObject->timestamp - $carbonObject->copy()->startOfDay()->timestamp;
if ($secondsPassed > 8 * 60 * 60) {
// time is passed 08:00 am
}
Today I've encountered something confusing for me with the behaviour of the \DateTime::createFromFormat function.
In my case I have a string, representing the date in the following format m/Y (05/2017). When I want to convert the string to DateTime object I've encountered the following issue:
$date = \DateTime::createFromFormat('m/Y', '02/2017');
When I dump the $date variable, the date property inside is '2017-03-03 11:06:36.000000'
But if I add the date before the month $date = \DateTime::createFromFormat('d/m/Y', '01/02/2017'); I get back an object with correct date property. (unfortunately I cant change the format of the date and add the day. It must be m/Y).
The fix I've come up with is to concatenate the first day of the month to the date string I have $date = '01/'.$dateString; but I rather not to do that because it's hardcoded.
What is wrong here? Does the createFromFormat function lack information of how to create the object? I'm quite confused with this. Thanks for everyone's help in advance!
By default, PHP will populate missing date values with those of the current date/time; so
$date = \DateTime::createFromFormat('m/Y', '02/2017');
will populate the missing day value with the current date; and as 31st February is an invalid date, it will roll forward into March. Likewise, hours/minutes/seconds will be populated with the missing time values based on the current time.
If you want to force the behaviour of forcing to the beginning of the month/time, then modify your mask with a leading !
$date = \DateTime::createFromFormat('!m/Y', '02/2017');
This will populate the missing day with the 1st of the month, and the time with 00:00:00
Alternatively, a trailing | will have the same effect
$date = \DateTime::createFromFormat('m/Y|', '02/2017');
You cannot store incomplete dates, not at least in a dedicated date format that can be used for complex date calculations (nothing prevents you from creating your own MonthYear class). So when you create a DateTime() object with incomplete information something needs to happen:
Crash
Use some default values
PHP opts for the second option and makes a decision inherited from the C language date library:
Assume that missing data means "now"
Try to fix automatically the invalid dates that this algorithm can create
In this case, Feb 2017 becomes 31 Feb 2017 (because "now" is 31 May 2017) and PHP follows this reasoning: February only had 28 days in year 2017 but I have three more; the user probably wants to move these three extra days into March. Thus 3 Mar 2017.
I see no reason to avoid hard-coding 01 because, after all, it is a hard-coded value (why the first day of the month and not the last one or any other day?).
$input = '05/2017';
$date = \DateTime::createFromFormat('d/m/Y', "01/$input");
My date formatted like this "2000-5-1". First digit represents 4 digit year. The second is number of week in year, and the last one represents number of day in week.
No matter what I do, function always returns false, my code is following:
date_create_from_format("Y-W-N", "2000-5-1")
(docs)
Please avoid solutions that are using magic words like "+1 day" etc.
Creating a DateTime object from a compound format with year/week/day is described in the compound formats section of the supported date and time formats section of the PHP Docs:
$x = new DateTime("2000-W05-1");
var_dump($x);
Note that the week number requires a leading zero
Result is a DateTime object for 2000-01-31
If you look at the documantation of DateTime::createFromFormat (which is what you are using with an alias) and date(), there is not a 100% overlap of the formats, so you have a problem.
This is what I found that you could use as an alternative: DateTime::setISODate
$date = explode('-','2000-5-1');
$newDate = new DateTime();
$newDate->setISODate($date[0], $date[1], $date[2]);
setISODate will take the year, week and day as explained in the documentation to create a DateTime object.
strtotime() returns number of seconds since so and so date. OK. So it's all in seconds. Now, if you give a date format which consists of only day, month and year, what time does it return in terms of seconds. The very first second of the day, the last second or undefined in between? The manual does not provide any guidance and common sense would assume the first second. Why is this significant? It could be when comparing or computing time interval between a fully defined date and a partially defined datetime (one without hours, minutes and seconds).
strtotime("1/1/2014")
Is this "guaranteed," as opposed to expected, to return the very first second of the new year?
It will return the time from 00:00:00, e.g. strtotime("1/1/2014"); = strtotime("1/1/2014 00:00:00");
In case you need to be sure, just use:
strtotime("1/1/2014 00:00:00");
Yes, it will always return first second of that day:
echo date('Y-m-d H:i:s', strtotime("1/1/2014"));
# 2014-01-01 00:00:00
demo
but to be sure, just enforce time like #Pekka suggested:
echo strtotime("1/1/2014 00:00:00");