【问题标题】:Incorrect timezone getting punched into db不正确的时区被打入数据库
【发布时间】:2017-08-15 16:18:48
【问题描述】:

我创建了一个表,该表的列具有日期时间和时区。 在我的 model.py 中:

db = SQLAlchemy(app)

class Messages(db.Model):
    __tablename__ = 'messages'
    msgId = db.Column('msg_id', db.Integer, primary_key=True)
    fullName = db.Column(db.String(60))
    message = db.Column(db.String)
    email = db.Column(db.String, unique=True)
    visitorId = db.Column(db.String(10))
    done = db.Column(db.Boolean)
    pub_date = db.Column(types.Time(timezone=True)) 

    def __init__(self, fullName, email, message, visitorId, submitTime):
        self.fullName = fullName
        self.email = email
        self.message = message
        self.visitorId = visitorId
        self.done = False
        self.pub_date = submitTime

我在heroku pg:psql 上使用db.create_all() 创建了表格,结果如下:

DATABASE=> \d messages
                                    Table "public.messages"
  Column   |         Type          |                         Modifiers                         
-----------+-----------------------+-----------------------------------------------------------
 msg_id    | integer               | not null default nextval('messages_msg_id_seq'::regclass)
 fullName  | character varying(60) | 
 message   | character varying     | 
 email     | character varying     | 
 visitorId | character varying(10) | 
 done      | boolean               | 
 pub_date  | time with time zone   | 

无论我使用哪个时区/日期时间,我使用的时区似乎都会受到+00 的打击,这是 UTC。

4 | ciasto       | testing    |          | 9IQVW1K6W | f    | 15:48:31.784704+00

为什么会这样?

我正在使用下面的路由函数来打发时间,该函数应该将正确的 dtWithZone 从下面传递到 postgreSQL 的表消息 pub_date

@app.route('/newmsg', methods=['GET', 'POST'])
def newmsg():
    form = _appforms.MessagingForm()
    if form.validate_on_submit():
        name = request.form['fullName']
        ip = request.access_route[0] # gives ip address of the visitor
        unique_visitor_id = request.cookies.get("unique_visitor")

        data = _utils.getJsonFromURL("http://ip-api.com/json/{}".format(ip))
        tz = "Asia/Kolkata"
        if data["status"] == "success":
            tz = data["timezone"]

        flash("Your IP is {} and you are visiting this site from {} timezone.".format(ip, tz))

        dtWithZone = datetime.datetime.now(pytz.timezone(tz))
        msg = Messages(name, request.form['email'], request.form['message'], unique_visitor_id, dtWithZone)
        _db.session.add(msg)
        _db.session.commit()
        msg = "Thank you, {}".format(name)
        return render_template('thankyou.html', form=form, msg=msg)
    return render_template('newMessage.html', form=form, title=" | Messaging", msg="Write your message")

但是,这在我的本地计算机上正常工作,并且查看表条目我可以看到:

  8 | tester | local teasing   |         | NV33A1L66 | f  | 21:09:24.804903+05:30

【问题讨论】:

  • 不是 MySQL。删除标签。

标签: postgresql datetime heroku flask


【解决方案1】:

自己想通​​了 <s>, since PostgreSQL timezone aware, if we punch datetime with timezone it converts it to PostgreSQL local timezone , so on heroku it is set to UTC so it was always getting converted to UTC format, so I converted the column pub_date to db.String(24) and saving date/time/ with timezone as string. which works perfect.

更新消息:

首先定义具有接受带有时区的日期时间的列的表:

src/model.py

from sqlalchemy import MetaData
from sqlalchemy import create_engine
from sqlalchemy import DateTime

db = SQLAlchemy(app)

class Messages(db.Model):
    __tablename__ = 'messages'
    msgId = db.Column('msg_id', db.Integer, primary_key=True)
    fullName = db.Column(db.String(60))
    message = db.Column(db.String)
    email = db.Column(db.String, unique=True)
    visitorId = db.Column(db.String(10))
    done = db.Column(db.Boolean)
    pub_date = db.Column(DateTime(timezone=True)) 

然后从 python 终端:运行from src.model import db; db.create_all() 这将在 postgresql 中创建具有正确列类型/名称的表。 然后有一个使用用户时区连接到 postgresSQL 的以下函数:

def insertIntoTable(dtWithZone, tableName, columValues):
    db_uri = "postgresql://localhost/messages"
    engine = create_engine(db_uri, connect_args={"options": "-c timezone={}".format(dtWithZone.timetz().tzinfo.zone)})
    meta = MetaData(engine, reflect=True)
    table = meta.tables[tableName]
    ins = table.insert().values(**columValues)
    conn = engine.connect()
    conn.execute(ins)
    conn.close()

就是这样!

现在我从路由功能做了以下更改:

@app.route('/newmsg', methods=['GET', 'POST'])
def newmsg():
    form = _appforms.MessagingForm()
    # if form.validate_on_submit():
    if request.method == "POST":
        ip = request.access_route[0]
        data = _utils.getJsonFromURL("http://ip-api.com/json/{}".format(ip))
        tz = "Pacific/America"
        if data["status"] == "success":
            tz = data.get("timezone")

        dtWithZone = datetime.datetime.now(pytz.timezone(tz))
        name = request.form.get('fullName', "")
        columValues = {
        'pub_date' : dtWithZone,  
        'fullName' : name,
        'visitorId': request.cookies.get("unique_visitor", ""),
        'message' : request.form.get('message', ""),
        'email' : request.form.get('email', "")
        }

        insertIntoTable(dtWithZone, 'messages', columValues)
        msg = "Thank you, {}".format(name)
        return render_template('thankyou.html', form=form, msg=msg)
    return render_template('newMessage.html', form=form, title=" | Messaging", msg="Write your message")

完美运行(y)

【讨论】:

  • 您还可以为每个连接设置时区,这将覆盖 postgresql 的内部时区。此外,您还可以执行“at time zone tznamehere”来说明它所在的时区。处理文本中的日期/时间戳是解决问题的好方法,因为时区偏移(如夏令时等)会发生变化。
  • 你的意思是说我为每个连接执行一个查询SET TIMEZONE TO 'time zone of the user'; ?
  • 你也可以做一些改变用户、改变数据库等的事情来让这些事情坚持下去。
  • 请注意,将用户的时区存储在某处也很有帮助。只是让 db 完成时区工作并知道它来自哪个 tz。在内部,pg 总是以 tz 为 utc 存储时间戳。
  • 不用担心我得到了它:db.Column(DateTime(timezone=True))
【解决方案2】:

所以这里有一个关于时区的简短教程。要查看 postgresql 在其内部 tz 数据库中的时区,请运行以下查询:

select * from pg_timezone_names ;
(edited list)
               name               | abbrev | utc_offset | is_dst
----------------------------------+--------+------------+--------
 America/Denver                   | MDT    | -06:00:00  | t
 America/Phoenix                  | MST    | -07:00:00  | f
 America/New_York                 | EDT    | -04:00:00  | t
 America/Chicago                  | CDT    | -05:00:00  | t
 America/Los_Angeles              | PDT    | -07:00:00  | t

下面是设置 tz 和插入时间戳的示例:

create table tztest (ts timestamptz, tz text);
set timezone="America/New_York";
insert into tztest values ('2017-01-10 12:00:00','America/New_York');
set timezone='America/Chicago';
insert into tztest values ('2017-01-10 12:00:00','America/Chicago');
set timezone='America/Denver';
insert into tztest values ('2017-01-10 12:00:00','America/Denver');
set timezone='America/Phoenix';
insert into tztest values ('2017-01-10 12:00:00','America/Phoenix');
set timezone='America/Los_Angeles';
insert into tztest values ('2017-01-10 12:00:00','America/Los_Angeles')
select * from tztest ;
           ts           |         tz
------------------------+---------------------
 2017-01-10 09:00:00-08 | America/New_York
 2017-01-10 10:00:00-08 | America/Chicago
 2017-01-10 11:00:00-08 | America/Denver
 2017-01-10 11:00:00-08 | America/Phoenix
 2017-01-10 12:00:00-08 | America/Los_Angeles

请注意,虽然 LA 被列为具有 -07:00:00 偏移量,但它不是夏天,所以现在是 -08:00:00 日期一月。

要查看实际存储的时间戳,请将时区设置为 UTC:

set timezone='UTC';
select * from tztest ;
           ts           |         tz
------------------------+---------------------
 2017-01-10 17:00:00+00 | America/New_York
 2017-01-10 18:00:00+00 | America/Chicago
 2017-01-10 19:00:00+00 | America/Denver
 2017-01-10 19:00:00+00 | America/Phoenix
 2017-01-10 20:00:00+00 | America/Los_Angeles

【讨论】:

  • 随时!时区数学相当古怪,所以如果可能的话,我总是让数据库处理。
  • 拥有另一列只是为了存储时区并没有什么坏处,所以我添加了一个新列只是为了在我的项目中存储用户时区。
猜你喜欢
  • 2021-04-23
  • 2012-05-10
  • 2019-03-21
  • 2013-11-16
  • 1970-01-01
  • 2016-08-24
  • 1970-01-01
  • 2011-06-27
  • 2017-02-18
相关资源
最近更新 更多