用时间和时区组织日期。 (转换为准确的时间戳。)

时间:2013-05-25 18:57:21

标签: php datetime timestamp timestamp-with-timezone

我已经用我的大脑挑选了几天,并遇到以下难题。

我基本上是以下复杂的mySQL表:

    | ID |   TITLE      |    DESCRIPTION    | EVENT |      DATE               |
    |----|--------------|-------------------|-------|-------------------------|
    |  1 | Big painting | 3-day work        |  No   | 03/10/2013              |
    |  2 | Meeting      | With reps.        |  Yes  | 02/15/2013 09:00 -05:00 |
    |  3 | Presentation | 5 paintings       |  Yes  | 08/02/2013 22:00 +02:00 |
    |  4 | Round paint. | One week          |  No   | 04/05/2013              |
    |  5 | Conference   | On Picasso        |  Yes  | 04/22/2013 18:00 -05:00 |

(编辑:也许我需要澄清DATE列未设置为DATE或DATETIME [由于需要在数据中包含时区]而是设置为VARCHAR,这就是组织复杂的原因从一开始。)

正如您所看到的,它是一张包含所有"什么是新的"例如,画家的物品。我们的想法是,在这个画家的网站上,PHP会调出数值,以便将它们显示为"什么是新的"新闻自动收报机。

所有的基础知识都存在,但我遇到了障碍。

最初,我想在SELECT阶段过滤和订购我的数据,但我在这方面遇到了麻烦。如您所见,事件和非事件项目具有日期,但非事件项目只是将它们作为组织整体数据的方式。具体的日期和时间仅对事件很重要,因为用户可以通过这种方式知道这些事情何时发生。所以基本的想法是从表中挑选出一定数量的最新项目。因此,所有项目都将从表中读取,按DATE的顺序排列,然后,比方说,其中20个将被带出。

正如我所说,我最初在SELECT阶段做过这个,但我认为这可能太复杂了。所以我只是提取了所有项目并在它们之后设置了一个PHP代码以便过滤它们。

因此,当我尝试订购日期时会出现障碍。我试图将日期转换为订购流程的时间戳,然后返回显示日期。但是,我无法将日期 - 时间戳或时间戳 - 日期(或两者)转换工作。我总是得到与我开始时不同的日期。

正如您所看到的,由于时区非常重要,整个事情变得更加复杂,因为用户必须能够知道这些事情发生在哪里,或者更确切地说,根据他们在何时何地# 39;重新发生。

我试过像这样来回转换:

    $timestamped = strtotime($date);
    $datetimed = date('m/d/Y h:i P',$timestamped);

它不起作用,所以我猜测它与我在桌子上使用的日期格式有关。

然后我尝试了这个:

    $var = DateTime::createFromFormat('m/d/Y H:i P',$date)->getTimestamp();

无济于事,但......

我想我也许应该在流程开始时设置时间戳,即插入数据项时。但是,在这里,我还需要转换一个"人类"日期到时间戳,如果我无法正确管理,那么任何东西都无法正常工作。

我理解这个问题很复杂,也许我的解释并不是最清楚的!也许输出示例可能有所帮助。我想要实现的目标是什么?"什么是新的"包含正在发生的事情的列表的新闻自动收报机:其中一些只是信息(比如状态更新,比方说),没有可见日期(这里的日期仅适用于内部组织;这是EVENT列的来源,因为它过滤哪些项目必须显示其日期,哪些必须不显示),其他项目,用户被邀请的实际事件以及显示用户日期的项目。我甚至有一个代码可以计算日期和时间是过去还是将来,以便显示" UPCOMING"标记那些尚未过去的事件。但是,我在处理日期和订购时遇到了问题。

这就是这个例子最后应该或多或少的样子:

Example of finished product

任何和所有帮助将非常感谢! (以及关于你们认为将是最实用,最干净/优雅/专业的处理这种数据检索/组织过程的方式的反馈......如果在数据输入时,如果在mySQL SELECT阶段,如果在以后,等等。 )

P.S。我可能会在同一个表中添加它来处理其他数据。这个特定的"什么是新的"数据由SELECT函数选择,该函数查找特定的WHATSNEW列,使其在任何行中具有TRUE值,该行将针对此特定的"什么是新的"新闻自动收报机。


决议(没有回答)

因为问题是关于将时间和时区组织为一个字符串,可以这么说,我不确定我是否可以将这两个优秀答案中的任何一个标记为对于该特定问题是正确的。我最终做的是剥离时区并将它们放在一个单独的列中。然后我将日期列格式化为Datetime。所以我已经解决了,因为mySQL Select可以为我处理订单。现在,关于时区,我最后作了一些作弊:我认为我们的"艺术家"不可能同时在两个不同时区的两个事件中进行,所以,实际上,我们需要订购两个如此接近的事件以使每个事件的时区成为一个事件,这是不太可能的。真正的差异首先出现。所以我只是按照日期对事物进行排序,然后用一个片段来处理时区并将它们变成#34; GMT + 1"," GMT-5"显示,以便用户知道当地时间的位置。我最终使用了DateTime::createFromFormat()->getOffset(),我现在看到了我推荐的第二个回答者,并说我走在正确的轨道上,所以我很高兴我保留了它为了进一步说明这一点,我添加了一个位置列,网站管理员可以在其中指定城市,说"巴黎",说"伦敦"等等。因此,用户最终会得到与我的示例中显示的内容非常相似的内容,除了它会说... (Paris, GMT+1)等等。

无论如何,对于那些有完全相同问题并最终想到完全相同的事情并且这种出路更实用的人来说,这就是我最终得到的代码的核心。其余的只是"填写"。请享用!感谢两位亲爱的人,他们非常善于从他们的日子里抽出时间来帮助我找到解决这个问题的方法! (我可能有一个额外的} ...对不起。当把它粘贴到SO中时重新格式化真的很乏味!我已经修改了两次代码而无法找到任何问题但是。)

if(isset($item) && $item['event'] == '1') {
  $event = $item['event'];
  $date  = $item['date'];

  $date_array = date_parse($date);

  $minute = $date_array['minute'];
  if($minute<10) {
    $minute = '0'.$minute;
  }

  $timezone = $item['timezone'];
  if($timezone!=='') {
    $timezone = DateTime::createFromFormat('P',$timezone)->getOffset();
    $timezone = $timezone/-3600;
    if($timezone<0) {
      $timezone = $timezone;
    } else
    if($timezone==0) {
      $timezone = '-0';
    } else {
      $timezone = '+'.$timezone;
    }
    $timezone = 'Etc/GMT'.$timezone;

    $timezone_real = $item['timezone'];
    $timezone_real = DateTime::createFromFormat('P',$timezone)->getOffset();
    $timezone_real = $timezone_real/-3600;
    if($timezone_real<0) {
      $timezone_real = str_replace('-','+',$timezone_real);//.':00';
    } else
    if($timezone_real==0) {
      $timezone_real = '+0';//:00';
    } else {
      $timezone_real = '-'.$timezone_real;//.':00';
    }
    $timezone_real = 'GMT'.$timezone_real;

    date_default_timezone_set($timezone);
  }

$today = date('n/j/Y G:i', time());
$today = strtotime($today);
$event_date = $date_array['month'].'/'.$date_array['day'].'/'.$date_array['year'].' '.$date_array['hour'].':'.$minute;
$event_date_unformatted = strtotime($event_date);

if($date_array['hour'] == '0') {
  $hour_convert = '12';
  $hour_suffix = 'a.m.';
} else if($date_array['hour']<12) {
  $hour_convert = $date_array['hour'];
  $hour_suffix = 'a.m.';
} else if($date_array['hour'] == '12') {
  $hour_convert = $date_array['hour'];
  $hour_suffix = 'p.m.';
} else {
  $hour_convert = $date_array['hour']-12;
  $hour_suffix = 'p.m.';
}

$date_convert = array('1'  => 'January', '2'  => 'February', '3'  => 'March', '4'  => 'April', '5'  => 'May', '6'  => 'June', '7'  => 'July', '8'  => 'August', '9'  => 'September', '10' => 'October',  '11' => 'November', '12' => 'December');

$event_date = $date_convert[$date_array['month']].' '.$date_array['day'].', '.$date_array['year'].', '.$hour_convert.':'.$minute.' '.$hour_suffix;

if(($event_date_unformatted-$today)>0) {
  echo '<h5>UPCOMING:</h5>';
  echo '<h6>'.$item['location'].', '.$event_date.' <sup>('.$timezone_real.')</sup></h6>';
}
}

2 个答案:

答案 0 :(得分:2)

您说DateTime::createFromFormat不起作用,但不要告诉我们错误消息是什么。我猜是因为你的格式字符串不代表你传递的格式。有关可接受的格式,请参阅the manual page

话虽如此,我相信你与DateTime::createFromFormat走在了正确的轨道上,这就是我要采取的方法。有一个比我更强大的SQL foo的人可能想出了一个用查询来做这个的方法,但这里是我纯粹的PHP方法来获取按日期排序的事件数组: -

//first we connect to our database
$dsn = 'mysql:dbname=stackoverflow;host=127.0.0.1';
$user = '********';
$password = '*********';

try {
    $dbh = new PDO($dsn, $user, $password);
} catch (PDOException $e) {
    echo 'Connection failed: ' . $e->getMessage();
}

//then we get our list of events from the table
$sql = "select * from datetable";

$select = $dbh->prepare($sql);
$select->execute();

//We now have an associative array of events, but in the wrong order.
$results = $select->fetchAll(PDO::FETCH_ASSOC);

$events = array();

//we want to sort on date, so lets get the dates into a format we can sort on
foreach($results as $result){
    $event = $result;
    $date = explode(' ', $result['date']);
    if(isset($date[1])){
        $event['date'] = \DateTime::createFromFormat('m/d/Y H:i', $date[0] . ' ' . $date[1]);
        $event['tz'] = $date[2];
    } else {
        $event['date'] = \DateTime::createFromFormat('m/d/Y', $date[0]);
    }
    $events[] = $event;
}

//our sorting function
$byDate = function(array $eventA, array $eventB){
    return $eventB['date'] > $eventA['date'] ? -1 : 1;
};

//sort the array
usort($events, $byDate);

//we now have our array sorted correctly
var_dump($events);

结果: -

array (size=5)
  0 => 
    array (size=6)
      'id' => string '2' (length=1)
      'title' => string 'Meeting' (length=7)
      'description' => string 'With reps.' (length=10)
      'event' => string 'Yes' (length=3)
      'date' => 
        object(DateTime)[4]
          public 'date' => string '2013-02-15 09:00:00' (length=19)
          public 'timezone_type' => int 3
          public 'timezone' => string 'Europe/London' (length=13)
      'tz' => string '-05:00' (length=6)
  1 => 
    array (size=5)
      'id' => string '1' (length=1)
      'title' => string 'Big painting' (length=12)
      'description' => string '3-day work' (length=10)
      'event' => string 'No' (length=2)
      'date' => 
        object(DateTime)[3]
          public 'date' => string '2013-03-10 23:18:05' (length=19)
          public 'timezone_type' => int 3
          public 'timezone' => string 'Europe/London' (length=13)
  2 => 
    array (size=5)
      'id' => string '4' (length=1)
      'title' => string 'Round paint.' (length=12)
      'description' => string 'One week' (length=8)
      'event' => string 'No' (length=2)
      'date' => 
        object(DateTime)[6]
          public 'date' => string '2013-04-05 23:18:05' (length=19)
          public 'timezone_type' => int 3
          public 'timezone' => string 'Europe/London' (length=13)
  3 => 
    array (size=6)
      'id' => string '5' (length=1)
      'title' => string 'Conference' (length=10)
      'description' => string 'On Picasso' (length=10)
      'event' => string 'Yes' (length=3)
      'date' => 
        object(DateTime)[7]
          public 'date' => string '2013-04-22 18:00:00' (length=19)
          public 'timezone_type' => int 3
          public 'timezone' => string 'Europe/London' (length=13)
      'tz' => string '-05:00' (length=6)
  4 => 
    array (size=6)
      'id' => string '3' (length=1)
      'title' => string 'Presentation' (length=12)
      'description' => string '5 paintings' (length=11)
      'event' => string 'Yes' (length=3)
      'date' => 
        object(DateTime)[5]
          public 'date' => string '2013-08-02 22:00:00' (length=19)
          public 'timezone_type' => int 3
          public 'timezone' => string 'Europe/London' (length=13)
      'tz' => string '+02:00' (length=6)

主要问题仍然是时区。您的数据库存储偏移量,而不是时区。关于SO(this one for example)有一些问题,所以我不会在这里加倍努力,但如果你找到了解决方法的话,阵列中可以获得补偿。

我在评论中注意到您正在考虑添加时区列,这是一个好主意。但是,我建议你将它们存储为来自this list的TZ字符串,然后可以将它们直接传递给DateTimeZone的构造函数,以便为您提供诸如节省日光等优点。

答案 1 :(得分:1)

SQL查询应该是

   $data= mysql_query( "SELECT * FROM (table name) ORDER BY date ASC")

我正在根据您的最新评论添加算法。

首先: 您的DATE列的数据类型必须是统一的,如果它是日期时间或时间戳格式,则应正确排序数据。

此链接为您提供日期和时间功能的完整列表。值得一读,让您更好地解决问题。

http://dev.mysql.com/doc/refman/5.5/en/date-and-time-functions.html

请参阅此链接以解决日期的正确格式 - (但是对于更干净的表格,我建议您只能在表格中接受一种格式):

Order by descending date - month, day and year

在线使用: 我想某种程度上你需要“标准化”时区。可以在同一时区内为所有事件创建时间戳,然后用户可以根据他们选择的时区查看这些时间,因此可以为每个用户调整所有时间。

此链接讨论时区的会话变量: http://dev.mysql.com/doc/refman/5.5/en/time-zone-support.html

  

每个连接时区。连接的每个客户端都有自己的时区设置,由会话time_zone变量给出。最初,会话变量从全局time_zone变量中获取其值,但客户端可以使用以下语句更改自己的时区:

     

的MySQL&GT; SET time_zone = timezone;

但是,对于本地使用,如果个人可能会收到更新的传单,并希望参加这些更新,那么您需要显示该地区的时区,并提及所在地点。添加例如GMT +2.00并不能让许多用户参考他们自己的时区来指示时间,他们通常必须自己进行转换。这给用户带来了额外的挫败感。为它们转换它是值得的,或者为时间差异提供一些解释 - 这样用户就可以清楚地了解这个事件发生在他们的时区的“何时”。

要处理时区,值得去这个链接: http://dev.mysql.com/doc/refman/5.5/en/date-and-time-functions.html#function_convert-tz

  

CONVERT_TZ()将日期时间值dt从from_tz给定的时区转换为to_tz给定的时区,并返回结果值。时区的指定如第10.6节“MySQL服务器时区支持”中所述。如果参数无效,则此函数返回NULL。

     

如果从from_tz转换为UTC时,该值超出了TIMESTAMP类型支持的范围,则不会发生转换。 TIMESTAMP范围在第11.1.2节“日期和时间类型概述”中进行了描述。

     

的MySQL&GT; SELECT CONVERT_TZ('2004-01-01 12:00:00','GMT','MET');            - &GT; '2004-01-01 13:00:00'   MySQL的&GT; SELECT CONVERT_TZ('2004-01-01 12:00:00','+ 00:00','+ 10:00');            - &GT; '2004-01-01 22:00:00'   注意   要使用“MET”或“Europe / Moscow”等命名时区,必须正确设置时区表。有关说明,请参见第10.6节“MySQL服务器时区支持”。

我认为如果您添加一个额外的列,这将有助于解决您的问题。

我想通过

  1. 检查您的数据类型
  2. 标准化您的时区
  3. 添加另一列以显示时区
  4. 您可以轻松订购餐桌。

    我希望这会有所帮助。请告诉我。