首页 文章

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

提问于
浏览
1

我一直在挑选我的大脑几天与以下难题 .

我基本上是以下复杂的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 |

(EDIT: Perhaps I need to clarify that the DATE column is not set as DATE or DATETIME [due to the need to include the Timezone in the data] but rather as VARCHAR, which is why the organization is complicated from the start.)

正如您所看到的,它是一张表格,其中包含了一位画家的所有“新内容”项目 . 我们的想法是,在这个画家的网站上,PHP会调出值,以便将它们显示为“What's New”新闻自动收录器 .

所有的基础都在那里,但我遇到了障碍 .

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

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

So the snag appears when I try to order the dates. I 'm trying to convert dates to timestamps for the ordering process, then back to dates for the displaying. However, I can' t使日期时间戳或时间戳日期(或两者)转换起作用 . 我总是得到与我开始时不同的日期 .

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

我试过像这样简单地来回转换:

$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. I might perhaps add that in the same table I handle other data. This specific "What's New" data is selected by a SELECT function that looks for a specific WHATSNEW column to have a value of TRUE in any row that will be retrieved for this specific "What's New" news ticker.


决议(没有回答)

因为问题是关于将时间和时区组织为一个字符串,可以这么说,我可能同时在两个不同时区的两个事件,所以,实际上,我们需要订购两个事件是不太可能的它们如此接近,以至于每个时区都会产生真正的差异 . 所以我只是通过日期对事物进行排序,然后掀起一个片段来处理时区并将它们转换为"GMT+1","GMT-5"显示,这样用户就会知道当地时间的位置 . 我最终使用 DateTime::createFromFormat()->getOffset() ,我现在看到我的第二个回答者推荐了,并说我在正确的轨道上,所以我很高兴我把它保留在那里为了进一步澄清这一点,我添加了一个位置列,其中网站管理员将能够指定城市,例如"Paris",说"London",依此类推 . 因此,用户最终会得到与我的示例中显示的内容非常相似的内容,除了它会说 ... (Paris, GMT+1) 等等 .

无论如何,对于那些有完全相同问题并最终想到完全相同的事情并且这种出路更实用的人来说,这就是我最终得到的代码的核心 . 其余的只是"fill-in" . 请享用!感谢两位亲爱的人,他们非常善于从他们的日子里抽出时间来帮助我找到解决这个问题的方法! (我可能还有一个额外的 } ...对不起 . 当把它粘贴到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 回答

  • 2

    你说 DateTime::createFromFormat 不't work, but don'告诉我们错误信息是什么 . 我猜是因为你的格式字符串不代表你传递的格式 . 有关可接受的格式,请参阅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

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

    我想如果你添加一个额外的列,这将有助于你的问题 .

    我想是的

    • 检查您的数据类型

    • 标准化你的时区

    • 添加另一列以显示时区

    您可以轻松订购餐桌 .

    我希望这有帮助 . 请告诉我 .

相关问题