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 |

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

如您所见,它是一个包含画家所有“最新消息”项目的表格。这个想法是,在这个画家的网站上,PHP 将调用值,以便将它们显示为“最新消息”新闻代码。

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

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

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

因此,当我尝试订购日期时,就会出现障碍。我正在尝试将日期转换为订购过程的时间戳,然后返回显示日期。但是,我无法使日期时间戳或时间戳日期(或两者)转换工作。我总是以与我开始时不同的日期结束。

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

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

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

而且它不起作用,所以我猜它与我在表中使用的日期格式有关。

所以我尝试了这个:

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

无济于事,还是...

我在想也许我应该在流程开始时设置时间戳,即在插入数据项时。但是,在这里,我也需要将“人类”日期转换为时间戳,如果我不能正确管理它,那么什么都不会正常工作。

我知道这个问题很复杂,也许我的解释不是最清楚的!也许输出示例可能会有所帮助。我想要实现的是“最新消息”新闻行情,其中包括正在发生的事情的列表:其中一些只是信息(比如状态更新,比如说)没有可见的日期(这里的日期仅适用于内部组织; 这是 EVENT 列的用武之地,因为它过滤了哪些项目必须显示其日期,哪些不能)、其他、邀请用户参加的实际事件以及向用户显示其日期的事件。我什至有一个代码可以计算日期和时间是过去还是未来,以便在那些尚未过去的事件上显示“即将到来”标签。但是,我在处理日期和订购日期时遇到了麻烦。

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

成品示例

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

PS我可能会在处理其他数据的同一张表中添加它。这个特定的“最新消息”数据由 SELECT 函数选择,该函数在将为此特定“最新消息”新闻代码检索的任何行中查找特定的 WHATSNEW 列以具有 TRUE 值。


解决方案(虽然没有回答)

因为问题是关于将时间和时区组织为一个字符串,所以可以这么说,那么我不确定我能否将这两个很好的答案中的任何一个标记为针对该特定问题的正确答案。我最终所做的是剥离时区并将它们放在单独的列中。然后我将日期列格式化为日期时间。所以我解决了这个问题,因为 mySQL Select 可以为我处理订单。现在,关于时区,我最终作弊了:我认为我们的“艺术家”不可能同时出现在两个不同时区的两个活动中,所以,真的,我们不太可能需要订购两个事件如此接近,以至于每个事件的时区都会产生真正的差异,而这首先发生。所以我只是让事情按日期排序,然后制作一个片段来处理时区并将它们变成“GMT+1”、“GMT-5”显示,这样用户就会知道那个位置在哪里当地时间。我最终使用DateTime::createFromFormat()->getOffset(),我现在看到我的第二个回答者推荐了它,并说我走在正确的轨道上,所以我很高兴我把它放在那里 为了进一步澄清这一点,我添加了一个 Location 列,网站管理员可以在其中指定城市,说“巴黎”,说“伦敦”,等等。所以用户最终会得到与我的示例中显示的非常相似的东西,除了它会说... (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>';
}
}
4

2 回答 2

2

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

话虽如此,我相信你在正确的轨道上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上有一些关于这个的问题(例如这个),所以我不会在这里加倍努力,但是如果你找到一种方法,可以在数组中使用偏移量。

我在您的评论中注意到您正在考虑添加时区列,这是一个好主意。但是,我建议您将它们存储为此列表中的 TZ 字符串,然后可以将它们直接传递到DateTimeZone的构造函数中,以便为您提供诸如允许夏令时等优势。

于 2013-05-25T22:32:56.250 回答
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

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

按日期降序排列 - 月、日和年

对于在线使用:我认为您需要以某种方式“标准化”时区。可以在同一时区内为所有事件创建时间戳,然后用户可以根据他们选择的时区查看这些时间,因此所有时间都针对每个用户进行调整。

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

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

mysql> SET time_zone = 时区;

但是,对于本地使用,个人可能会收到更新的传单,以便参加他们,那么您需要显示该地区的时区,并提及该地区。例如,添加 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 类型的支持范围,则不会发生转换。第 11.1.2 节“日期和时间类型概述”中描述了 TIMESTAMP 范围。

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 服务器时区支持”。

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

我认为通过

  1. 检查您的数据类型
  2. 标准化您的时区
  3. 添加另一列以显示时区

您可以轻松订购餐桌。

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

于 2013-05-25T19:12:44.127 回答