|
|
转自 http://cao7113.blog.sohu.com/147243545.html
刚接触rails时,有时看到插入数据库的created_at字段与本地时间不一致,未深究。最近几个项目集成时,又遇相关问题,本文为调查记录,相关点:
本地时间-即观察者使用的当地时间(按地理标准时区概念)
操作系统时区设置
程序(Rails/Java)时区设置
数据库(MySql)时间字段的时区标准/设置
相关资料:
概念:
UTC-世界标准时间
LT-本地时间
CST-Central Standard Time, time zone offset: utc-6 hours
CST is used during winter in these US states(CDT during summer)...
计算机时间:主板上有个时钟发生器,用于计时。可通过CMOS设置时间(硬件时间)。Windows和Linux/Unix/Mac对硬件时间的理解不同,前者把它理解为LT(含时区值),后者理解为UTC,系统显示的时间是累加时区后的时间,网上很多人装双系统遇到时间不一致,就是这个原因。系统开机时读取硬件时间,关机时将对应值重新写入。
OS上的软件,一般都基于OS的时区设置,有些允许独立设置,如MySql,以支持世界各种时间变化,存储的时间字段也基于此时区设置。
Rails程序中的时区概念只是其间转换的工具(程序控制级别)
MySql:
MySQL 时区默认是服务器时区
可以通过以下命令查看
SQL代码
1. mysql> show variables like '%time_zone%';
2. +------------------+--------+
3. | Variable_name | Value |
4. +------------------+--------+
5. | system_time_zone | CST |
6. | time_zone | SYSTEM |
7. +------------------+--------+
8. 2 rows in set (0.00 sec)
可以通过修改my.cnf
在 [mysqld] 之下加
default-time-zone=timezone
来修改时区。如:
default-time-zone = '+8:00'
改了记得重启msyql喔!
另外也可以通过命令 set time_zone = timezone
比如北京时间(GMT+0800)
set time_zone = '+8:00';
Rails:
rails时区相关的rake tasks: time:zones:all/us/local
caoruijian@caoruijian-desktop:$ rake --tasks time:
(in /home/caoruijian/Aptana RadRails Workspace/web)
rake time:zones:all # Displays names of all time zones recognized by the Rails TimeZone class, grouped by offset.
rake time:zones:local # Displays names of time zones recognized by the Rails TimeZone class with the same offset as the system local time
rake time:zones:us # Displays names of US time zones recognized by the Rails TimeZone class, grouped by offset.
rails相关类:TimeZone(2.1版本后),timezone是rails2.1后新加的特征,并对ruby的Time做了扩展
Time.zone可以查看时区,Time.zone=可以设置
Time.now显示本地时区的时间值
项目可能遇到的一个问题是:在rails中看到的是本地时间(如北京时间),利用Model.create方式存入数据库(Mysql)后看到的是UTC时间(早八个小时),完成的一个测试实验如下:
环境:
OS: Ubuntu, Timezong: Shanghai(同Beijing,东8区)
Rails: environment.rb中设置config.time_zone = 'Beijing'#UTC
MySql: my.cnf中设置default-time-zone = '+8:00'(现在作用还不明)
操作:在rails的action中调用User.create往数据库表自动插入一条记录,现在为早上09:24左右(系统显示的本地时间),rails生成的Sql为:
Processing TestController#test (for 127.0.0.1 at 2010-03-31 09:24:20) [GET]
\u001B[4;36;1mUser Columns (0.6ms)\u001B[0m \u001B[0;1mSHOW FIELDS FROM `users`\u001B[0m
\u001B[4;35;1mSQL (0.0ms)\u001B[0m \u001B[0mBEGIN\u001B[0m
\u001B[4;36;1mUser Create (0.9ms)\u001B[0m \u001B[0;1mINSERT INTO `users` (`name`, `created_at`, `updated_at`) VALUES(NULL, '2010-03-31 01:24:20', '2010-03-31 01:24:20')\u001B[0m
\u001B[4;35;1mSQL (34.9ms)\u001B[0m \u001B[0mCOMMIT\u001B[0m.
可见,rails在往数据库发送数据前就做了某些转换,实验中将Beijing时区的时间转换为UTC的啦。可见问题出在rails的ActiveRecord的转换机制上,那究竟是怎么回事呢?网搜后看到有人设置了:
config.active_record.default_timezone = 'Beijing'#:local (environment.rb中)
呵呵,实验后果然看起来正常啦!问题应该是ActiveRecord默认使用UTC时区时间,默认会在你设置的时区和UTC间转换。由此更能体会到ActiveRecord作为程序和数据库间连接纽带的作用
源码解析:(rails-2.3.4/lib/initialize.rb Rails::Initializer#initialize_time_zone)
# Sets the default value for Time.zone, and turns on ActiveRecord::Base#time_zone_aware_attributes.
# If assigned value cannot be matched to a TimeZone, an exception will be raised.
def initialize_time_zone
if configuration.time_zone
zone_default = Time.__send__(:get_zone, configuration.time_zone)
unless zone_default
raise \
'Value assigned to config.time_zone not recognized.' +
'Run "rake -D time" for a list of tasks for finding appropriate time zone names.'
end
Time.zone_default = zone_default
if configuration.frameworks.include?(:active_record)
ActiveRecord::Base.time_zone_aware_attributes = true
ActiveRecord::Base.default_timezone = :utc #呵呵,找到啦!
end
end
end
那前文提到的MySql时区设置看起来为啥没起作用呢?问题还在于自己的理解上,default-time-zone = '+8:00',只在数据库级别上使用,并将任何输入作为该时区的时间值进行相关时间操作,跟这里要说的问题没什么关系
以上为临时记录,待进一步整理/更新,可能涉及的相关问题有:
* rails时间/时区机制与原理,当使用ActiveRecord时对数据库时间的影响?
* 当设定了某一时区时,对rails时间函数/时间比较逻辑的影响,如Time.now, Time.zone.now分别是那个时间?编写程序时是否要有时区意识,是否要修改遗留代码?
* 系统如何处理多时区/地区/语言问题,更复杂的设置?可能关系到服务器上程序的国际/区域化管理 |
|