Parsing only Time using Carbon - php

I have a field in my DTO class which accepts start_time and end_time as "2:00 AM"
/**
* #var string
*/
#[CastWith(TimeCaster::class)]
public string $start_time; // 01:00 AM example
/**
* #var string
*/
#[CastWith(TimeCaster::class)]
public string $end_time;
Can I parse this format of time using Carbon in my Caster Class
#[\Attribute] class TimeCaster implements Caster
{
public function cast(mixed $value): mixed
{
return Carbon::parse($value)->format();
}
}

I think you use Carbon::createFromFormat
Carbon::createFromFormat('H:i A','10:00 PM')->format('Y-m-d H:i:s)
if you want to get only time using timestamp then
Carbon::parse("2021-06-26 22:00:00")->format('g:i A')

Related

Schedule change while copying entities to next week cause a shift

I have a bug on one of my feature with timezone, let me explain.
The goal of this feature is to copy user events of a week to the next week.
I have a UserEvent model with this properties
/**
* #ORM\Entity(repositoryClass="AppBundle\Repository\UserEventRepository")
*/
class UserEvent
{
/**
* #ORM\Id()
* #ORM\Column(type="integer")
* #ORM\GeneratedValue(strategy="AUTO")
*/
private $id;
/**
* #ORM\Column(type="datetime")
*/
private $start;
/**
* #ORM\Column(type="datetime")
*/
private $end;
/**
* #ORM\ManyToOne(targetEntity="UserEventType")
* #ORM\JoinColumn(nullable=false)
*/
private $type;
/**
* #ORM\ManyToOne(targetEntity="User", inversedBy="userEvents")
* #ORM\JoinColumn(nullable=false)
*/
private $user;
/* GETTERS AND SETTERS */
}
The datetime are stored in datebase in UTC timezone, and at the moment the client using the application is in the Europe/Paris timezone.
This is the logic to copy events to the next week
$newEvents = [];
foreach ($eventsToCopy as $event) {
$newEvent = clone $event;
$newEvent->getStart()->modify('+1 week');
$newEvent->getEnd()->modify('+1 week');
$newEvents[] = $newEvent;
}
The weekend are exlude from the week
The problem appear on this weeks
WEEK A: 2017-10-23 to 2017-10-27
WEEK B: 2017-10-30 to 2017-11-03
There is a time change schedules between week A and week B therefore the planified hours on the second week are shifted on hour forward.
I can't do a high level change to handling DateTime and timezone, I can only modify this code (legacy application)
I'm not very confortable with timezone, maybe I'm missing something obvious.
Please give me your magic guideline to fix this case ! :)
I solved my problem by:
Converting UTC from database to Europe/Paris
Do my stuff to modify the datetime like I want
Then I convert back my Europe/Paris datetime to UTC and save it in database
Code example
$newEvent = new UserEvent();
$newEvent->setUser($event->getUser());
$newEvent->setType($event->getType());
// Convert the datetimes of event to copy to Europe timezone
// For my case I have only one timezone to handle at the moment, so it's always about Europe/Paris <=> UTC
// In advanced use case you could convert the UTC datetime to the user's timezone
list($start, $end) = $this->timezoneFormater->convertUTCDatetimeToEuropeDatetime([$event->getStart(), $event->getEnd()]);
// Now set the date
$start->modify('+1 week');
$end->modify('+1 week');
// Then convert back to UTC timezone
list($start, $end) = $this->timezoneFormater->convertEuropeDatetimeToUTCDatetime([$start, $end]);
$newEvent->setStart($start);
$newEvent->setEnd($end);
$newEvents[] = $newEvent;

DateTime timestamp does not change when changing timezone

Alright so basically, I am a little confused about how the timestamp works in DateTime in PHP. I wish to make two methods that convert from a local time to UTC and vice versa.
I currently have this:
/**
* #param \IPS\DateTime $utcDateTime The UTC datetime.
* #param \DateTimeZone $timezone The timezone to convert the UTC time to.
* #return \IPS\DateTime New datetime object in local datetime.
* #throws \Exception when the UTC date is not in UTC format. (debugging purposes)
*/
public static function utcToLocal($utcDateTime, $timezone)
{
if ($utcDateTime->getTimezone()->getName() !== "UTC") {
throw new \Exception("Date time is not UTC!");
}
$time = new DateTime($utcDateTime, new \DateTimeZone("UTC"));
$time->setTimezone($timezone);
return $time;
}
/**
* #param \IPS\DateTime $localDateTime A datetime configured with the the user's timezone
* #return DateTime New datetime object in UTC format
* #throws \Exception When given datetime is already in UTC (for debugging purposes)
*/
public static function localToUtc($localDateTime)
{
if ($localDateTime->getTimezone()->getName() === "UTC") {
throw new \Exception("Value is already UTC");
}
$time = new DateTime($localDateTime, $localDateTime->getTimezone());
$time->setTimezone(new \DateTimeZone("UTC"));
return $time;
}
When I debug this code, at the last line return $time in localToUtc(...) my debugger shows the correct conversions:
However, when I evaluate the expression
$localDateTime->getTimestamp() === $time->getTimestamp()
it will return true.
So I am a little confused, I just want the timestamps to change when I change the timezone. I am thinking maybe I need to work with getOffset() but I want to make sure I do it in the correct way. I'd also prefer not to use any string format tricks because I feel like that is not the correct way.

How can I change more than a date format in a unique format with a single php function?

I would like to understand how to change different date formats into a single format using a php function. After trying in every way, i wasn't able to solve this puzzle. I always just used the following code in my custom function.php smoothly:
/* Change date format for coupon */
function change_date_format($x) {
$date = DateTime::createFromFormat('j-M-Y', $x);
$x = $date->format('Y-m-d');
return $x;
}
In this way i can convert the format 'j-M-Y' in the format 'Y-m-d'. The problem is that now i need to convert not only the date format 'j-M-Y', but also other formats (for example, i've to convert date format 'j-M-Y' and date format 'Y-m-d\TH:i:sP' in date format 'Y-m-d'. I tried to combine different logic functions but system gives me error.
Thanks to all of you who try to help me...
The DateTime class is pretty good at parsing different formats without createFromFormat(). If the formats you have are supported (Supported Date and Time Formats) then just let it create based on the in-built logic. If $x = '2016-06-30T23:59:59+02:00' then the DateTime class handles this just fine:
function change_date_format($x) {
$date = new DateTime($x);
return $date->format('Y-m-d');
}
Add an input parameter to your function called: $inputFormat and use this instead 'j-M-Y', so you should specify always the input format. You can specify a default format for input.
/**
* Return with a normal format of any date by given format
* Default format is j-M-Y if no input format given
*
* #param string $dateString
* #param string $inputFormat
* #return string
*/
function change_date_format($dateString, $inputFormat = 'j-M-Y') {
$date = DateTime::createFromFormat($inputFormat, $dateString);
return $date->format('Y-m-d');
}
echo change_date_format('23-05-2016', 'd-m-Y');
echo change_date_format('05/23/2016', 'm/d/Y');
You can use an additional parameter as follows:
/*Change date format for coupon*/
function change_date_format($x, $dateFormat) {
$date = DateTime::createFromFormat($dateFormat, $x);
$x = $date->format('Y-m-d');
return $x;
}

How to store a duration with Doctrine ODM

I stored documents which represent events with doctrine. Each $event has an $eventType. Each $eventType has a $duration.
For each $event I will only store the $begin but not the end as it can be calculated with the default $duration of its $eventType.
What would be the most elegant way to store the duration with each eventType?
I would choose a DateInterval annotation if it existed in doctrine. But it does not.
Save the number of seconds of the duration as Integer?
Any other possibility?
The simplified eventType Model looks like this
<?php
/**
* Class EventType
* #ODM\Document(collection="EventType")
*/
class EventType {
/**
* #var $duration int
* #ODM\Int
*/
private $duration;
}
As you mentioned, you can save the duration in seconds.
It's pretty straightforward to have a getEnd method:
/**
* return \DateTime
*/
public function getEnd()
{
$end = clone $this->begin;
return $end->add(new DateInterval('PT' . $this->duration . 'S'));
}
Or you could store the $end date, and not the duration, which you could also calculate:
/**
* return \DateInterval
*/
public function getDuration()
{
return $begin->diff($end);
}
The tricky thing with date differences is the daylight saving time: sometimes you can have
date + 3600seconds == date + 2*3600seconds
So you should think what kind of queries you will make for the end date: will you select events lasting (more or less than) a given amount of time, or will you select events ending (before/after/at) a given date?
Second option is more common, so I would store the end date.

Timezone Datetime with offset in php , getting it easy

I have timestamp from specific timezone (Jamaica) and i want to get GMT timestamp of it. Is there more elegant solution than this one :
$start = DateTime::createFromFormat('U', 1330560000);
$start->setTimezone(new DateTimeZone('America/Jamaica'));
$start->format('Y-m-d H:i:s');//2012-02-29 19:00:00 NO NO NO
$tz = new DateTimeZone( 'America/Jamaica' );
$transition = $tz->getTransitions($start->getTimestamp(),$start->getTimestamp());
$offset = $transition[0]['offset'];
$start = DateTime::createFromFormat('U', $params['start'] - 2*$transition[0]['offset']);
$start->setTimezone(new DateTimeZone('America/Jamaica'));
$start->format('Y-m-d H:i:s'); // "2012-03-01 05:00:00" YESSSS!!!
Unix time, or POSIX time, is a system for describing instances in time, defined as the number of seconds that have elapsed since midnight Coordinated Universal Time (UTC), 1 January 1970.
source Wikipedia
The idea of the UNIX timestamp is that it is always in UTC (everywhere in the world xD ). If it does not represents the time in UTC it's not a UNIX timestamp anymore
This is a part of my class that create a well-formatted time stamp as I wrote in the comment of the function, it's very easy to use just pass the string of the date, time zone and the identifier.
Hope it helps you
/**
* Default timestamp format for formatted_time
* #var string
*/
public static $timestamp_format = 'Y-m-d H:i:s';
/**
* Returns a date/time string with the specified timestamp format
* #example $time = Date::formatted_time('5 minutes ago');
* #link http://www.php.net/manual/datetime.construct
* #param string $datetime_str datetime string
* #param string $timestamp_format timestamp format
* #param string $timezone timezone identifier
* #return string
*/
public static function formatted_time($datetime_str = 'now', $timestamp_format = NULL, $timezone = NULL){
$timestamp_format = ($timestamp_format == NULL) ? Date::$timestamp_format : $timestamp_format;
$timezone = ($timezone === NULL) ? Date::$timezone : $timezone;
$tz = new DateTimeZone($timezone ? $timezone : date_default_timezone_get());
$time = new DateTime($datetime_str, $tz);
if ($time->getTimeZone()->getName() !== $tz->getName()){
$time->setTimeZone($tz);
}
return $time->format($timestamp_format);
}

Categories