Convert timezone offset to hours & minutes preserving sign - php

Suppose I have an offset like this:
$secOffset = -28800
I need it to convert in a format that is usable with MYSQL function convert_tz()
If I use gmdate("h:i", secOffset) I got 16:00 that is almost correct but It miss the minus - (that is quite important)
Alternative approach:
Suppose I have a time string America/Los_Angeles and I want to convert in a offset useful for MYSQL function convert_tz()
The final expected output is the offset from GMT so given a timezone.
For ex, having a timezone like America/Los_Angeles, the output should be:
−08:00
I'm on Magento/Zend so if any available function is on Zend I can accept answer based on it.

You should use the DateTimeZone and DateInterval classes for this:
$tzid = 'America/Los_Angeles';
$tz = new DateTimeZone($tzid);
$date = new DateTime('now', $tz);
// create a new date offset by the timezone offset
// gets the interval as hours & minutes
$offset = $tz->getOffset($date) . ' seconds';
$dateOffset = clone $date;
$dateOffset->sub(DateInterval::createFromDateString($offset));
$interval = $dateOffset->diff($date);
$formatted = $interval->format('%R%H:%I');
This is a little convoluted, as you first get the timezone offset in seconds, and then use DateTime to help convert that interval into hours/mins.

Related

Convert User Submitted Date To UTC

I'm trying to figure out how to accept a date/time from a form, which is consequently in the user's timezone, and change it to UTC before inserting it into the database. For some reason, no amount of searching has netted me an answer.
My form will POST whatever date is selected by the user to my code, so I expect to be able to do something like this. Note: the $userDate may be relative to any number of timezones based on user's location
$userDate = $_POST['user_date'] // 2014-05-15 16:37:23
I anticipate using Date().getTimezoneOffset() on my form to also submit the users UTC offset (as detailed here).
$userOffset = $_POST['user_offset']
Then before inserting the date into my database, I would like to convert it to UTC -- but I am stumped on how to do that with PHP (I'm actually using Laravel so if you know of a way using Carbon, that would be even easier, but I couldn't find it in their docs).
I've been half tempted to manually parse the offset and convert it to number of seconds and add or subtract it to strtotime() output of the $userDate and then convert it back into a date format using date() -- but there has to be a better way!
What am I missing here? Does PHP have a function I just don't know about that lets me do something like:
$userDate = '2014-05-15 16:37:23';
$userOffset = '+04:00';
$utcDate = date_apply_offset($userDate, $userOffset);
echo $utcDate; // Outputs: 2014-05-15 20:37:23
Or am I making this harder than it has to be?
EDIT
Based on the solution provided by #vascowhite, I went with the following (added into question to improve answers for those seeking guidance)
I ended up using a function from moment.js since I was already using it to convert UTC to user's timezone on display.
HTML:
<input id="user_offset" type="hidden" name="user_offset" value="">
Javascript:
var offset = moment().format('ZZ');
$('#user_offset').val(offset);
PHP (in a custom date class):
class MyDate {
/**
* Convert Date to UTC
*
* #param string $date Any date parsable with strtotime()
* #param string $offset UTC offset of date
*/
public static function toUTC($date, $offset = '+0:00')
{
if ($timestamp = strtotime($date) && ! empty($offset) )
{
$newDate = date('Y-m-d H:i:s', $timestamp);
$newDate = new \DateTime($date . ' ' . $offset);
$newDate->setTimezone(new DateTimeZone('UTC'));
$date = $newDate->format('Y-m-d H:i:s');
}
return $date;
}
}
// To convert
$userDate = trim($_POST['user_offset']);
$userOffset = trim($_POST['user_date']);
$utc = MyDate::toUTC($userDate, $userOffset)
That class method isn't perfect, and in the event something goes wrong, it just returns the date back -- when really it should throw an exception.
This is a simple task with the DateTime classes:-
$userDate = '2014-05-15 16:37:23';
$userOffset = '+04:00';
$date = new \DateTime($userDate . ' ' . $userOffset);
var_dump($date);
$date->setTimezone(new \DateTimeZone('UTC'));
var_dump($date);
You can then format the date as you wish for output eg:-
echo $date->format('Y-m-d H:i:s');
or:-
$utcDate = $date->format('Y-m-d H:i:s');
echo $utcDate; // Outputs: 2014-05-15 20:37:23
See it working.
If you are doing any work with dates and times in PHP it is worth taking the time to become familiar with these extremely useful classes.
For all sorts of date/time manipulations you can make use of moment.php
For your example all what is needed are two lines of code:
$m = new \Moment\Moment('2014-05-15 16:37:23', '+0400');
echo $m->setTimezone('UTC')->format(); // 2014-05-15T12:37:23+0000
There is much more which helps to deal with date/time issues: https://github.com/fightbulc/moment.php
Cheers

Creating timestamp for 5/13/2014 # 3pm

I have an application that needs to send a UTC timestamp in order for it to work correctly. In my application a user can have any number of timezones. So if they pick 3pm and their timezone is America/New_York, it is a different 3pm than if it was America/Chicago.
I need to figure out a way to change the date into the right UTC timestamp. I know I can use date_default_timezone_set("UTC")...but I don't think will work correctly.
I think I need to calculate a difference between UTC and regular timezone, but I am not sure. Any advice is welcomes.
date_default_timezone_set("UTC");
echo strtotime('5/13/2014 3:00 PM');
1399993200
date_default_timezone_set("America/New_York");
echo strtotime('5/13/2014 3:00 PM');
1400007600
As you can tell these 2 values are different.
EDIT: Here is what my code looks like. It doesn't seem to work correctly as the application doesn't show the event in the right time.
$previous_timezone = date_default_timezone_get();
date_default_timezone_set("UTC");
$aceroute_schedule = $this->sale_lib->get_send_to_aceroute_schedule();
if (($start_time = strtotime($aceroute_schedule['aceroute_schedule_date']. ' '.$aceroute_schedule['aceroute_schedule_time_start'])) !== FALSE)
{
//Append 000 as as string for 32 bit systems
$start_epoch = $start_time.'000';
$end_epoch = strtotime('+ '.$aceroute_schedule['aceroute_duration'].' minutes', $start_time).'000';
}
else //Default to current time + 1 hour
{
//Append 000 as as string for 32 bit systems
$start_epoch = time().'000';
$end_epoch = strtotime('+1 hour', time()).'000';
}
$event->start_epoch = $start_epoch;
$event->end_epoch = $end_epoch;
Update:
This will now create a DateTime object in the user's DateTimeZone ('America/New_York'). And then it will set that object's timezone to UTC. To get the timestamp (or other string representations of date), use ::format().
# Create NY date
$NY = new DateTimeZone("America/New_York");
$NYdate = new DateTime('5/13/2014 3:00 PM', $NY);
# Set timezone to UTC
$UTC = new DateTimeZone("UTC");
$UTCdate = $NYdate->setTimezone($UTC);
# Get timestamp (PHP 5.2 compatible)
$timezone = $UTCdate->format('U');
var_dump($timezone); // a string containing UNIX timestamp
First I create 2 DateTime objects based off of their respective DateTimeZone objects. Then we can either use OOP ::diff() to get another object containing information about the time difference. Or we can use simple integers representing the difference in seconds from ::getTimestamp.
$date = '5/13/2014 3:00 PM';
# Create NY date
$NY = new DateTimeZone("America/New_York");
$NYdate = new DateTime($date, $NY);
# Create UTC date
$UTC = new DateTimeZone("UTC");
$UTCdate = new DateTime($date, $UTC);
# Find difference object
$diff = $NYdate->diff($UTCdate);
var_dump($diff); // a DateInterval object containing time difference info
# Find difference in seconds
$diff = $NYdate->getTimestamp() - $UTCdate->getTimestamp();
var_dump($diff); // an integer containing time difference in seconds
Links:
DateTimeZone
DateTime
DateInterval
Example in http://www.php.net/manual/en/datetime.settimezone.php
$date = new DateTime('2000-01-01', new DateTimeZone('Pacific/Nauru'));
echo $date->format('Y-m-d H:i:sP') . "\n";
$date->setTimezone(new DateTimeZone('Pacific/Chatham'));
echo $date->format('Y-m-d H:i:sP') . "\n";
The first line creates a DateTIme object, using the timezone Pacific/Nauru.
You can then change the timezone using setTimezone as shown in line 4, and the output will be modified accordingly.
note: the default timezone (if you don't specify it in the 2nd parameter in line 1) is the one set in your php.ini file, which you can modify (at runtime) with date_default_timezone_set("America/New_York")
note2: the 1st parameter in line 1, is equivalent to the 1st parameter of the strtotime function.
note3: the format method takes the same format parameter as date (http://www.php.net/manual/en/function.date.php)

How to return ISO date format in PHP for MongoDB?

I want to store the current date generated from PHP into MongoDB collection as an ISO date formate.
ISODate("2012-11-02T08:40:12.569Z")
However I am not able to generate such Kind of date in php which will be stored in MongoDB as an ISODate format.
This is what I ve done.
$d = new MongoDate(time());
echo $d;
and it is outputting something like,
0.00000000 1353305590
which is not the format I need. How to do this?
You could run the __toString function, or use the sec field
__toString will return a timestamp in usecs, which you can pass to date() after separating the seconds from milliseconds - read here: http://us1.php.net/manual/en/mongodate.tostring.php
OR, I personally prefer to have mongodb return just the seconds, which can be plugged directly into date() - read here: http://php.net/manual/en/class.mongodate.php
Also, if you're generating a MongoDate() for right now, you don't need to specify time();
In order to return an isodate, you need to do this:
echo date(DATE_ISO8601, (new MongoDate())->sec);
...
$exampleDate = new MongoDate();
echo date(DATE_ISO8601, $exampleDate->sec);
EDIT: To save your ISO date, you need to do the following:
$mongoDateObject = new MongoDate(strtotime("2012-11-02T08:40:12.569Z"));
For clarity, let's consider the following use case:
You need to convert a string in the simplified extended ISO 8601 format (e.g. returned by Javascript's Date.prototype.toISOString()) to and from PHP's MongoDate object, while preserving maximum precision during conversion.
In this format, the string is always 24 characters long: YYYY-MM-DDTHH:mm:ss.sssZ. The timezone is always zero UTC offset, as denoted by the suffix Z.
To keep milliseconds, we'll have to leverage PHP's DateTime object.
From string to MongoDate:
$stringDt = "2015-10-07T14:28:41.545Z";
Method 1 (using date_create_from_format):
$phpDt = date_create_from_format('Y-m-d\TH:i:s.uP', $stringDt);
$MongoDt = new \MongoDate($phpDt->getTimestamp(), $phpDt->format('u'));
Method 2 (using strtotime):
$MongoDt= new \MongoDate(strtotime ($stringDt),
1000*intval(substr($stringDt, -4, 3)) // cut msec portion, convert msec to usec
);
From MongoDate to string:
$MongoDt = new \MongoDate(); // let's take now for example
$stringDt =
substr(
(new \DateTime())
->setTimestamp($MongoDt->sec)
->setTimeZone(new \DateTimeZone('UTC'))
->format(\DateTime::ISO8601),
0, -5) // taking the beginning of DateTime::ISO8601-formatted string
.sprintf('.%03dZ', $MongoDt->usec / 1000); // adding msec portion, converting usec to msec
Hope this helps.
convert ISO date time in UTC date time here :
$timestamp = $quicky_created_date->__toString(); //ISO DATE Return form mongo database
$utcdatetime = new MongoDB\BSON\UTCDateTime($timestamp);
$datetime = $utcdatetime->toDateTime();
$time=$datetime->format(DATE_RSS);
$dateInUTC=$time;
$time = strtotime($dateInUTC.' UTC');
$dateInLocal = date("d M Y", $time);
echo $dateInLocal; die;
You can convert ISODate time by using below code.
* return ISO-8601 date format:YYYY-MM-DD'T'HH:mm:ss.sssXXX , for example: 2015-09-07T10:13:45.110-07:00 .
*/
date("Y-m-d\TH:i:s.000P", strtotime($date));

Converting to the same timezone in php

Strangeness. It's not an edge case. Rather, let's say my server stores it's date/time in America/Toronto. I then run the it through time conversion logic incase, say, you're on the west coast. Here's the code I got:
$timestamp = '2012-07-25 16:30:00';
$to = 'America/Toronto';
$from = 'America/Toronto';
// system timezone
$system = (new DateTimeZone($from));
// desired conversion timezone
$desired = (new DateTimeZone($to));
// timestamp DateTime object
$resource = (new DateTime($timestamp, $system));
// offset
$offset = $desired->getOffset($resource);
print($offset);
The offset that is getting printed at this point is -14440 (4 hours). I don't imagine the system or database timezones are coming in here (both of which are set to America/Toronto). Any light would be appreciated on this. Confusing :(
DateTimeZone::getOffset() returns the offset in seconds from GMT (-14440 = 4 hours for America/Toronto).
Edit:
Apologies for my initial confusion with DateTime::getOffset()!
Anyway, to address the title of your question, use DateTime::setTimeZone() to convert between timezones.
This is probably what you're looking for, the offset between the 2 timezones.
// system timezone
$system = (new DateTimeZone($from));
// desired conversion timezone
$desired = (new DateTimeZone($to));
// timestamp DateTime object
$resource = (new DateTime($timestamp, $system));
$desiredDateTime = (new DateTime($timestamp, $desired));
// offset
$offset = $desired->getOffset($desiredDateTime) - $system->getOffset($resource);
print($offset);

Converting GMT time to local time using timezone offset, not timezone identifier

It's pretty easy to convert a given GMT date into local time if you're given the timezone identifier from this list in PHP: http://www.php.net/manual/en/timezones.php
For example, you can do this (where $fromTimeZone is just 'GMT', $toTimeZone is just one of the constants from that list (i.e. 'America/Chicago'), and $datetime is the GMT date):
public static function convertToTimezone($datetime, $fromTimeZone, $toTimeZone, $format = 'Y-m-d H:i')
{
// Construct a new DateTime object from the given time, set in the original timezone
$convertedDateTime = new DateTime($datetime, timezone_open($fromTimeZone));
// Convert the published date to the new timezone
$convertedDateTime->setTimezone(timezone_open($toTimeZone));
// Return the udpated date in the format given
return $convertedDateTime->format($format);
}
However, I'm having issue converting the same GMT date to the local time if just given the timezone offset. For instance, instead of being given 'America/Chicago', I'm given -0500 (which is the equivalent offset for that timezone).
I've tried things such as the following (where $datetime is my GMT date and $toTimeZone is the offset (-0500 in this case)):
date($format, strtotime($datetime . ' ' . $toTimeZone))
I know all the date() sort of functions are based on the servers's timezone. I just can't seem to get it to ignore that and use a timezone offset that is given explicitly.
You can convert a specific offset to a DateTimeZone:
$offset = '-0500';
$isDST = 1; // Daylight Saving 1 - on, 0 - off
$timezoneName = timezone_name_from_abbr('', intval($offset, 10) * 36, $isDST);
$timezone = new DateTimeZone($timezoneName);
Then you can use it in a DateTime constructor, e.g.
$datetime = new DateTime('2012-04-21 01:13:30', $timezone);
or with the setter:
$datetime->setTimezone($timezone);
In the latter case, if $datetime was constructed with a different timezone, the date/time will be converted to specified timezone.

Categories