How to display converted time zones in a 'gene

2019-06-08 03:57发布

We are building a scheduling application wherein one user may set his "general availability" for all weeks like the following:

Sunday | Monday | ... | Friday | Saturday

When we ask a person A in India to indicate his "availability", we ask him to select from a drop down of values something like this:

12:00am
12:30am
01:00am
...
11:30pm

We ask him to select BOTH the "From" time (starting) and the "Till" time (ending).

What we SAVE in the database is JUST these values (see the following example):

user_id avail_day   from        to  
1     Sunday        12:00:00    12:15:00
2     Monday        12:00:00    12:15:00

So, in essence, it looks like the following (in his LOCAL time zone)

(A)
Sunday | Monday | ... | Friday | Saturday
-----------------------------------------
       |        |     |        | 8:30am to 10:30am

As a separate piece of information, we know that he has selected to work in the IST (Indian Standard Time), which is presently GMT + 5:30 hours, so we can assume that the values he chooses are FOR the time zone he's presently in.

Now, for a person B on the East Coast, which is presently GMT - 4 hours (EDT), this time would be actually

Friday, 23:00:00 to Saturday, 01:00:00

We need help in figuring out how to:
(a) convert the earlier "text value" of the person A in IST to the local value of the EST person (NOTE that we know JUST the day and hours of availability as TEXT values)
(b) AND, then, we need to figure out how to display it on a "Standard week" beginning on a Sunday and ending on a Saturday. What we want displayed should be something like this:

(B)
Sunday | Monday | ... |       Friday       |      Saturday
--------------------------------------------------------------
       |        |     | 11:00pm to 12:00am | 12:00am to 1:00am

Any smart ways of converting (A) into (B)?


Artefacto's code made into a generic function (Revision 2)

// This function should be used relative to a "from_date"
// The $from_timebegin and $from_timeend MUST be for the same day, not rolling over to the next
function shift_timezones_onweek3($from_timezone, $from_date, $from_timebegin, $from_timeend, $to_timezone)
{
    $tz1 = new DateTimezone($from_timezone);

    $datetime1 = new DateTime("$from_date $from_timebegin", $tz1);
    $datetime2 = new DateTime("$from_date $from_timeend", $tz1);

    $interval = $datetime1->diff($datetime2);

    $indiaAvail = array(
        array($datetime1, $datetime2)
    );


    $tz2 = new DateTimezone($to_timezone);
    //convert periods:
    $times = array_map(
        function (array $p) use ($tz2) {
           $res = array();
           foreach ($p as $d) {
               $res[] = $d->setTimezone($tz2);
           }
           return $res;
        },
        $indiaAvail
    );

    $res = array();
    foreach ($times as $t) {
        $t1 = reset($t);
        $t2 = next($t);
        if ($t1->format("d") == $t2->format("d")) {
            $res[$t1->format("l")][] = $t1->format("g:ia") . " to ".
                $t2->format("g:ia");
        }
        else {
            $res[$t1->format("l")][] = $t1->format("g:ia") . " to 11:59pm";
            $res[$t2->format("l")][] = "12:00am to ". $t2->format("g:ia");
        }
    }

    return $res;
}

3条回答
对你真心纯属浪费
2楼-- · 2019-06-08 04:14

Use PHP's build-in DateTime class to do timezone conversions. Save the data in UTC to the database. When displaying your standard week schedule, use local timezone. To handle ±23 hours of timezone differences, you'll have to query 9 days (±1 days) from the DB before conversion.

Edit: To convert times to current user's local time, you need to get the timezone of each event. Join the scheduled events to user information for the user who made the event. This way you'll have the timezone from which to convert to the user's timezone.

The following pseudoish PHP will show:

 $usersTZ = new DateTimeZone('EDT');
 $now = new DateTime("now", $usersTZ);
 $today = $now->format("Y-m-d");
 $sql = "SELECT A.date, A.start, A.end, B.tz FROM schedule A JOIN users B ON (schedule.user_id = users.user_id) WHERE A.date BETWEEN '$sunday_minus_1_day' AND '$saturday_plus_1_day' ORDER BY A.date, A.start";
 foreach ($db->dothequery($sql) as $event) {
     $eventTZ = new DateTimeZone($event['tz']);
     $eventStartDate = new DateTime("$today {$event['start']}", $eventTZ);
     $eventStartDate->setTimeZone($usersTZ);
     $eventEndDate = /* do the same for the end date */
     if ($eventStartDate->format("Y-m-d") != $eventEndDate->format("Y-m-d")) {
          /* create 2 events */
     } else {
          /* save the event to list of events with the new start and end times */
     }
 }
 /* sort events, their order may be different now */

Of course, it would all be a lot simpler if you could save the start and end times with TZ to the DB and let the DB do all the hard work for you.

查看更多
等我变得足够好
3楼-- · 2019-06-08 04:25

Your question doesn't make sense considering weekdays in the vacuum. These weekdays must be actual days, because the time conversion rules change along the year (DST) and through the years (politicians sometimes change the timezones and/or the date in which DST starts/ends).

That said, let's say you have you have a week availability plan for the first week of August, here defined as the week Aug 1 to Aug 7 2010:

<?php
$tz1 = new DateTimezone("Asia/Calcutta");
$indiaAvail = array(
    new DatePeriod(new DateTime("2010-08-01 10:00:00", $tz1),
        new DateInterval("PT2H15M"), 1),
    new DatePeriod(new DateTime("2010-08-07 03:00:00", $tz1),
        new DateInterval("PT8H"), 1),
);


$tz2 = new DateTimezone("America/New_York");
//convert periods:
$times = array_map(
    function (DatePeriod $p) use ($tz2) {
       $res = array();
       foreach ($p as $d) {
           $res[] = $d->setTimezone($tz2);
       }
       return $res;
    },
    $indiaAvail
);

$res = array();
foreach ($times as $t) {
    $t1 = reset($t);
    $t2 = next($t);
    if ($t1->format("d") == $t2->format("d")) {
        $res[$t1->format("l")][] = $t1->format("g:ia") . " to ".
            $t2->format("g:ia");
    }
    else {
        $res[$t1->format("l")][] = $t1->format("g:ia") . " to 11:59pm";
        $res[$t2->format("l")][] = "12:00am to ". $t2->format("g:ia");
    }
}

print_r($res);

gives

Array
(
    [Sunday] => Array
        (
            [0] => 12:30am to 2:45am
        )

    [Friday] => Array
        (
            [0] => 5:30pm to 11:59pm
        )

    [Saturday] => Array
        (
            [0] => 12:00am to 1:30am
        )

)

This may put in the same basket weekdays that are actually different days, but there's obviously no way to avoid it without explicitly indicating the day (or adding something like "Saturday (week after)" and "Saturday (week before)". This appears to be what you want, though.

查看更多
The star\"
4楼-- · 2019-06-08 04:41

How about this:

  • Ask user to provide his time zone (maybe you can even detect it by getting the user's location based on IP address, but the user still might want to change it)
  • Convert the provided time to you webserver's time and save it.
  • When displaying the time convert to the viewing user's time zone.

The Pear::Date package may help you doing this:
http://www.go4expert.com/forums/showthread.php?t=3494

Hope this helps, Manuel

查看更多
登录 后发表回答