PostgreSQLã§ã¿ã€ã ã¹ã¿ã³ããæäœãããããã¯ã¯ãã€ã³ã¿ãŒãããäžã®ãã·ã¢èªå°éã®åºçç©ã§ã¯ããŸãé瀺ãããŠããããããã°ã©ããŒã®ä»äºã«ãããé »ç¹ãªåé¡ã®åå ã§ãã 人æ°ã®ããå€åœã®ããã°depesz.comã®èè
ã§ããHubert Lubaczewskiã®è³æã®ç¿»èš³ã«æ³šç®ããŸãã ãã®èšäºãã圹ã«ç«ãŠã°å¹žãã§ãïŒ
æã
ãIRCãŸãã¯ã¡ãŒãªã³ã°ãªã¹ãã®èª°ãããç¹ã«ã¿ã€ã ãŸãŒã³ãèæ
®ããã¿ã€ã ã¹ã¿ã³ãã®æ·±ã誀解ïŒãŸãã¯çè§£äžè¶³ïŒã瀺ã質åãããŸãã æ¢ã«ããã«åºããããã®ã§ãã¿ã€ã ã¹ã¿ã³ããšã¯äœããããããã©ã®ããã«äœ¿çšãããããããŠééããå¯èœæ§ã®ããæãäžè¬çãªé害ã¯äœããæããŠãã ããã
䜿çšã§ãã2çš®é¡ã®ããŒã¿ããããŸãã
- ã¿ã€ã ã¹ã¿ã³ã
- ã¿ã€ã ãŸãŒã³ä»ãã¿ã€ã ã¹ã¿ã³ãïŒãŸãã¯timestamptzïŒ
ã¿ã€ã ã¹ã¿ã³ãã¿ã€ãã«ã¯æ¥ä»ãšæå»ã®ã¿ãå«ãŸããä»ã®æ
å ±ã¯å«ãŸããŸããã äžæ¹ã§ãããã¯æªãããšã§ã¯ãªãããã«èŠãããããããŸããïŒçµå±ãæ¥åžžç掻ã§ã¯æ¥ä»ãšæå»ã®ã¿ã䜿çšããŸããïŒïŒããããäžæ¹ã§ãããã¯å
šãã®ææã§ãã
ã¿ã€ã ã¹ã¿ã³ããã2014-04-04 20:00:00ãã§ãããšæ³åããŠãã ãããæ®å¿µãªãããããã»ã©å€ãã¯ãããŸããã 4æã¯ãããµã³ãŒã«ã¹ãã·ã«ãŽããã³ãã³ãã¯ã«ã·ã£ã¯ããŸãã¯ã¢ã¹ã¯ã¯ã®ç°ãªãæç¹ã§ãããããã¯ã¿ã€ã ãŸãŒã³ã®åé¡ã§ãã
ãã¡ãããããªãã¯æ¬¡ã®ããã«æããããããŸããããç§ã¯åžžã«åãã¿ã€ã ãŸãŒã³ã«ããŸããç°ãªãã¿ã€ã ãŸãŒã³ã®ãµããŒãã«èŠåŽããå¿
èŠã¯ãããŸããã ç§ã®ã¿ã€ã ãŸãŒã³ã§ã¯ãç¹å®ã®æç¹ãããŒã¯ããã®ã«æ¥ä»ãšæå»ã§ååã§ããããã¯ããŸãã«ãå®éã®ç掻ãã§è¡ãããšã ããã§ãã
ããããæ¬åœã«ããã§ããïŒ
ã2013-10-27 02:00:00ããšããã©ãã«ããããã¢ããªã±ãŒã·ã§ã³ãããŒã©ã³ãæéã«é¢é£ããŠããããšãããã£ãŠãããšããŸãã ãã®å Žåãåå2æã®äžå€®ãšãŒããã倿éïŒCESTïŒããŸãã¯éåžžã®äžå€®ãšãŒãããæéããã1æéé·ããªãå¯èœæ§ãããããã幞éã§ã¯ãªããªããŸãã å£ç¯ããšã®æéã®å€åãåå ã§ãã
ã¿ã€ã ãŸãŒã³ãªãã§ã¿ã€ã ã¹ã¿ã³ãã䜿çšããããšã¯ã»ãšãã©ã®å Žåãã°ã§ãããä¿®æ£ããå¿
èŠããããšæããŸãã ã¢ããªã±ãŒã·ã§ã³ã®ãšã³ããªãç°ãªãã¿ã€ã ãŸãŒã³ïŒã¹ã±ãžã¥ãŒã©ã¢ããªã±ãŒã·ã§ã³ãªã©ïŒããã®ãã®ã§ããå Žåã¯ãããã«å€ãã®åé¡ããããŸãã
ãã®ãããæãæãããªè§£æ±ºçã¯ãã¿ã€ã ãŸãŒã³ïŒã¿ã€ã ã¹ã¿ã³ãïŒã§ã¿ã€ã ã¹ã¿ã³ãã䜿çšããããšã§ãã
ãŸããããã¯ããå€ãã®ãã£ã¹ã¯ã¹ããŒã¹ãå æããŸããïŒ
$ select typname, typlen from pg_type where typname ~ '^timestamp'; typname | typlen
ããã¯ã©ã®ããã«æ©èœããŸããïŒ ã©ãã«ã¯ã¿ã€ã ãŸãŒã³ãèªèããŠããå¿
èŠããããŸããããªããããããå€ãã®ã¹ããŒã¹ãå¿
èŠãšããªãã®ã§ããïŒ
äºå®ã¯ã圌女ã¯ã¿ã€ã ãŸãŒã³ãç¥ããªããšããããšã§ãã å
éšã§ã¯ãtimestamptzåã®ãã¹ãŠã®å€ã¯UTCïŒåå®äžçæïŒã§ãã
UTCã«ã¯åªããæ©èœããããŸãããªãã»ããã¯ãªãïŒããèªäœãä»ã®ã¿ã€ã ãŸãŒã³ã®ãªãã»ãããèæ
®ãããéå§ç¹ã§ãïŒã倿éãšå¬æéã®éã«éãã¯ãããŸããã ãããã£ãŠãUTCã¿ã€ã ã¹ã¿ã³ãã¯åžžã«1ã€ã®æç¹ã®ã¿ãæãããšãä¿èšŒãããŠããŸãã
ããããåžžã«UTCã§ç€ºãå Žåãå¿
èŠãªã¿ã€ã ãŸãŒã³ã®æéãã©ã®ããã«èŠã€ããã®ã§ããïŒ
timestamptzå€ã«ãªããã³ã«ãã¿ã€ã ãŸãŒã³ãæå®ãããŠããªãå ŽåãPostgreSQLã¯äºåã«èšå®ãããæéã䜿çšããŸãã ãŸããããŸããŸãªæ¹æ³ã§æ§æã§ããŸãã
- postgresql.confã®timezoneãã©ã¡ãŒã¿ãŒ
- ããŒã¿ããŒã¹ã倿ŽããŸã...ã¿ã€ã ãŸãŒã³ãèšå®ããŸã= '...'
- ãŠãŒã¶ãŒã®å€æŽ... set timezone = '...'
- SETã¿ã€ã ãŸãŒã³= '...'
æåã®ãã©ã¡ãŒã¿ãŒã¯ããµãŒããŒãé
眮ãããŠããã¿ã€ã ãŸãŒã³ã瀺ãããã«äœ¿çšãããŸãã ã€ãŸããä»ã®å€æŽããªãå Žåã«äœ¿çšãããããã©ã«ãã®ã¿ã€ã ãŸãŒã³ã
次ã®2ã€ã®æ¹æ³ã¯ãéžæããããŒã¿ããŒã¹ãšãŠãŒã¶ãŒã®ããã©ã«ãå€ã倿ŽããŸãã
åŸè
ã®æ¹æ³ã¯ãããŒã¿ããŒã¹æ¥ç¶ãããŸããŸãªèšå®ã§æ©èœãããå Žåã«äœ¿çšã§ããŸãã
nowïŒïŒã®åºåãã©ã®ããã«å€åããããèæ
®ããŠãã ããïŒ
$ select now(); now
ãããã£ãŠãtimestamptzå€ã衚瀺ãŸãã¯å€æŽãããã³ã«ãPostgreSQLã¯ããããUTCãã/ã«å€æããŸãã
ããã¯ãå€ãç°¡åã«æ¯èŒã§ããããšãæå³ããŸãïŒãããã¯ãã¹ãŠåãã¿ã€ã ãŸãŒã³ã«ããã倿éãŸãã¯å¬æéã®ã·ããããªããããåžžã«æ¯èŒãå¯èœã§ãïŒã
ãããŠãå€ãã2014-04-04 20:00:00ãã§ãããšæ³åããŠã¿ãŸãããã ãŸããä»åã¯ããµã³ãŒã«ã¹ã«ããããšãç¥ã£ãŠããŸãããå¥ã®ã¿ã€ã ãŸãŒã³ã§åäœããããŒã¿ããŒã¹ã«ä¿åããå¿
èŠããããŸãã çŸåšã®ãªãã»ããã-7æéã§ããããšã確èªãããã®å€ã次ã®ããã«äœ¿çšã§ããŸãã
$ select '2014-04-04 20:00:00-07'::timestamptz; timestamptz
ã©ããã ååŸ8æã«è¡šç€ºãããªãã®ã¯ãªãã§ããïŒ
çç±ã¯ç°¡åã§ã-ããã¿ã€ã ãŸãŒã³ã§ãªã¯ãšã¹ãã«ã¿ã€ã ã¹ã¿ã³ããæ¿å
¥ããŸããã å
éšã§ã¯ãã©ãã«ã¯UTCã«å€æããããã®åŸãéåžžã®ã¿ã€ã ãŸãŒã³ã«å倿ãããŸãïŒãããããUTCããªããŠãããããŸããïŒã
$ show timezone; TimeZone
ããµã³ãŒã«ã¹ã®ã¿ã€ã ãŸãŒã³ãèšå®ããå Žåãã¯ãšãªçµæã¯æ¬¡ã®ããã«ãªããŸãã
$ set timezone = 'America/Los_Angeles'; SET $ select '2014-04-04 20:00:00-07'::timestamptz; timestamptz
衚瀺ãããå€ã¯åžžã«ã¿ã€ã ãŸãŒã³èšå®ãèæ
®ããããšãçè§£ããããšãéèŠã§ãã
ããµã³ãŒã«ã¹ã§20:00ãååŸããå¥ã®æ¹æ³ããããŸãã
$ set timezone = 'Poland'; SET $ select '2014-04-04 20:00:00'::timestamp at time zone 'America/Los_Angeles'; timezone
å€ã®åŸã«ã::ã¿ã€ã ã¹ã¿ã³ããã远å ããããšã¯éåžžã«éèŠã§ãããã以å€ã®å Žåã¯ãå¥åŠãªãã®ã«ãªããŸãã
$ set timezone = 'Poland'; SET $ select '2014-04-04 20:00:00' at time zone 'America/Los_Angeles'; timezone
ããã§äœãèµ·ãã£ãã®ã§ããïŒ 11:00ã¯ã©ãããæ¥ãã®ã§ããïŒ
åŒçšç¬Šã§å²ãŸããå€ïŒ2014-04-04 20:00:00ïŒã¯timestamptzãšããŠèªèãããŸããããã¯ãç§ã®ã¿ã€ã ãŸãŒã³ã§ååŸ8æãæå³ããŸãã
select '2014-04-04 20:00:00'::timestamptz; timestamptz
ãããŠãå€ãç§ã®ã¿ã€ã ãŸãŒã³ã«è»¢éãããåŸã«ã®ã¿ãPGã¯ãat time zone ...ããèªã¿åããŸããããã¯ãéžæããã¿ã€ã ãŸãŒã³ã®æå»ã衚瀺ããããã«äœ¿çšãããŸãã
ãããã£ãŠãã¿ã€ã ãŸãŒã³ã®ã¿ã€ã ã¹ã¿ã³ãã¯å€timestamptzãæäŸããŸããããã¯ãéžæãããã¿ã€ã ãŸãŒã³ã®çŸå°æéãã³ãã³ãã§æå®ãããæå»ã瀺ããŸãã
ã¿ã€ã ãŸãŒã³ã®timestamptzã¯ã¿ã€ã ã¹ã¿ã³ãå€ãæäŸããæå®ãããæéã«éžæãããã¿ã€ã ãŸãŒã³ã®æéã衚瀺ããŸãã
ããã¯å°ããããã«ããã®ã§ãäŸãæããŸãããã
select '2014-04-04 20:00:00'::timestamptz at time zone 'UTC'; timezone
è峿·±ãããšã«ãPgãã¿ã€ã ãŸãŒã³ã«å«ãŸããŠããªããŠããããã䜿çšããŠãããã¿ã€ã ãŸãŒã³ããå¥ã®ã¿ã€ã ãŸãŒã³ã«æéã倿ã§ããŸãã
ããµã³ãŒã«ã¹ã®äœæãã¢ã¹ã¯ã¯ã®æ8æããç¥ããããšããŸãã ç§ã®çŸå°æéã¯æ¬¡ã®ãšããã§ãã
$ show timezone; TimeZone
ããã«ããã¡ãªããã¯ã»ãšãã©ãããŸããã
ãŸããã¢ã¹ã¯ã¯ã®åå8æã瀺ãç¹å®ã®æç¹ïŒtimestamptz圢åŒïŒãç¹å®ããå¿
èŠããããŸãã
$ select '2014-04-04 08:00:00'::timestamp at time zone 'Europe/Moscow'; timezone
ããã¯ãã¿ã€ã ãŸãŒã³ã®åå6ã«å¯Ÿå¿ããããšã瀺ããŠããŸãã ããããããµã³ãŒã«ã¹ã®æéãç¥ãããã§ãã ãLAãã®ã¿ã€ã ãŸãŒã³ã§ã2014-04-04 06ïŒ00 + 02ããšæžãããšãã§ããŸãããå¥ã®æ¹æ³ã§è¡ãããšãã§ããŸãã
$ select ('2014-04-04 08:00:00'::timestamp at time zone 'Europe/Moscow') at time zone 'America/Los_Angeles'; timezone
åŒ 'timestamp at time zone ..'ã¯timestamptzãšåãã§ããããããat time zoneããåã³äœ¿çšããŠãã¿ã€ã ãŸãŒã³ãæå®ããã«ã¿ã€ã ã¹ã¿ã³ãã«å€æãçŽããŸããå¥ã®å Žæã«ã
ããã§ãã¹ãŠãæããã«ãªãããšãé¡ã£ãŠããŸãã ç§èªèº«ã¯ããªãé·ãéãã®åé¡ãæŽçããããšããŠããŸããããæçµçã«ãã¹ãŠãçè§£ããŸãã:)
ããã«ã¯è峿·±ãå¯äœçšã1ã€ãããŸããtimestamptzã§æ©èœãã颿°ã«ã€ã³ããã¯ã¹ã远å ããã®ã¯ããã»ã©ç°¡åã§ã¯ãããŸããã ããšãã°ãææ¥ãååŸããããã«äœ¿çšãããã€ã³ããã¯ã¹ãäœæããããšã¯ã§ããŸããã
$ create table test (i timestamptz); CREATE TABLE $ create index q on test (to_char(i, 'Day')); ERROR: functions in index expression must be marked IMMUTABLE
äžèšã®äŸã«ç€ºãããã«ãçç±ã¯éåžžã«åçŽã§ããåãæç¹ã¯ãã¿ã€ã ãŸãŒã³ã«å¿ããŠç°ãªãææ¥ãåç
§ã§ããŸãã ãŸããto_charïŒïŒã¯çŸåšã®ã¿ã€ã ãŸãŒã³ã䜿çšãããããã·ã¹ãã ã®ã¿ã€ã ãŸãŒã³èšå®ã«å¿ããŠãåããœãŒã¹ããŒã¿ã«å¯ŸããŠç°ãªãå€ãçæã§ããŸãã
$ set timezone = 'Europe/Warsaw'; SET $ insert into test (i) values ('2014-04-04 06:00:00'); INSERT 0 1 $ select i, to_char(i, 'Day') from test; i | to_char
ãŸã£ããåãæç¹ã ããç°ãªãæ¥ã å Žæã«å¿ããŠãç°ãªãæãŸãã¯ç°ãªã幎ã«ãªããŸãã
ããã®ã¿ã€ã ã¹ã¿ã³ãïŒã¿ã€ã ãŸãŒã³ãªãïŒã¯åŒ·ãåŽé¢ãã瀺ããŠããŸãã-ã¿ã€ã ãŸãŒã³ãæå®ããŠããªããããæ
å ±ã®æœåºã«å®å
šã«äœ¿çšã§ããŸãã
ããããtimestamptzãtimestampã«å€æããæ¹æ³ã¯ç¥ã£ãŠããŸãã ããªãã¯åœŒå¥³ã«ã¿ã€ã ãŸãŒã³ãäŒããå¿
èŠããããŸãã ãããã£ãŠããããè¡ãããšãã§ããŸãã
create index q on test (to_char(i at time zone 'Poland', 'Day'));
ããããæ®å¿µãªãããããã¯äœãçããŸããã å®éã®ãšãããto_charã¯å€çšéã§ãã to_charã¯æ¬¡ã®ããã«äœ¿çšã§ããŸãã
$ select to_char(now(), 'TMMonth'); to_char
ä»åã¯ãã¿ã€ã ãŸãŒã³ã®ããã§ã¯ãªãããã±ãŒã«ã®ããã«ä»ã®çµæãåŸãããŸãã
ã€ã³ããã¯ã¹äœæã®åé¡ã«å¯Ÿããæ£ãã解決çã¯ã絶察ã«äžå®ã®ãç°å¢ãã§to_charãåŒã³åºããŠããã§ã«ã€ã³ããã¯ã¹ãäœæããç¬èªã®é¢æ°ãäœæããããšã§ãã ãã®ããã«ïŒ
create function day_from_ts(timestamptz) returns text as $$ select to_char( $1 at time zone 'Poland', 'Day' ); $$ language sql immutable; CREATE FUNCTION
ããã§ãã€ã³ããã¯ã¹äœæã«äœ¿çšã§ããŸãã
create index q on test (day_from_ts( i )); CREATE INDEX
ããã¯ã颿°èªäœãã¿ã€ã ãŸãŒã³ããããŒã©ã³ããã«èšå®ãããã±ãŒã«å€ãç¡èŠããããã«to_charãåŒã³åºãïŒã€ãŸããto_char圢åŒã«ã¯TMãã¬ãã£ãã¯ã¹ããªãïŒãããå®å
šã§ãã
åœç¶ããã®ã€ã³ããã¯ã¹ã䜿çšããã«ã¯ããã¹ãŠã®ã¯ãšãªã§æ¬¡ã®é¢æ°ã䜿çšããå¿
èŠããããŸãã
select * from test where day_from_ts(i) = 'Friday';
ã¿ã€ã ãŸãŒã³ã§äœæ¥ããå Žåã®ãã1ã€ã®éèŠãªããšã¯ãUnixããæéãååŸããããšããŸãã¯ããããæä»£ã§ãã äžè¬çã«ãããã¯ç°¡åã§ãïŒ
$ select extract(epoch from now()); date_part
è峿·±ãããšã«ãã¿ã€ã ãŸãŒã³ã«äŸåããŸããã
$ begin; BEGIN $ show timezone; TimeZone
誰ããç¥ã£ãŠããããã§ã¯ãªããããã®çç±ã¯ãUnixæéã¯åžžã«UTCã¿ã€ã ãŸãŒã³ã§åãå
¥ããããããã§ãã ããã¯ãã¿ã€ã ã¹ã¿ã³ããããšããã¯ãååŸãããšãã«ãPGã¯UTCã§ãããšæ³å®ããããšãæå³ããŸãã æ¬¡ã®æœåšçãªåé¡ãçºçããåå ïŒ
$ select now(), extract(epoch from now()); now | date_part
æåã®ã±ãŒã¹ã§ã¯ãPgã¯ããã€ã³ãã€ã³ã¿ã€ã ããåãåããŸããããã¯å
éšçã«UTCã«å€æãããŸãïŒè¡šç€ºããããšãç§ã®ã¿ã€ã ãŸãŒã³+2ã«å€æãããŸãïŒã
2çªç®ã®å Žåãã¿ã€ã ã¹ã¿ã³ãã¯ç§ã®ã¿ã€ã ãŸãŒã³ã«ãããŸãããUTCïŒå€æãªãïŒïŒã§ãããšæ³å®ãããŸãããŸãã '2014-04-04ã§ã¯ãªãã' 2014-04-04 21ïŒ19ïŒ01.456205 UTC 'ã®å€ããæä»£ãååŸãããŸãã 21ïŒ19ïŒ01.456205 + 02 'ã
ã ãã¬ãŒãã
èŠããã«ãã¿ã€ã ã¹ã¿ã³ããé¿ããtimestamptzã䜿çšããããã«ããŠãã ããã
æåŸã«èšãããã®ã¯ããã°ãæœåšçãªåé¡ã§ã¯ãªããå€ãã®äººãæ°ä»ããŠããªãæ©èœã§ãã
ããããã®ããã«ãPostgreSQLã¯ãã€ã¯ãç§åäœã®æ£ç¢ºãªã¿ã€ã ã¹ã¿ã³ãïŒããã³timestamptzïŒã䜿çšããŸãã ç§ã¯å人çã«ã¯ããã奜ãã§ã¯ãããŸããããå€ãã®äººã¯ç²ŸåºŠãæå€§1ç§ã«éããªããšäž»åŒµããŠããŸãã
ã¿ã€ã ã¹ã¿ã³ããštimestamptzïŒããã³æéã«é¢é£ããä»ã®ã¿ã€ãã®ããŒã¿ïŒã®äž¡æ¹ã«ã远å ã®ç²ŸåºŠïŒã粟床ãïŒãèšå®ã§ããŸãã
ç°¡åãªäŸãæããŸãããã
$ select now(), now()::timestamptz(0), now()::timestamptz(1); now | now | now
ãã¡ãããããŒãã«ã§ãããã䜿çšã§ããŸãã
$ create table test (i timestamptz(0)); CREATE TABLE $ insert into test(i) values (now()); INSERT 0 1 $ select * from test; i
ãããïŒ ãnowïŒïŒããªã©ã倿Žããå¿
èŠã¯ãããŸãããããŒã¿åã«ç²ŸåºŠã远å ããã ãã§ããã¹ãŠã調æŽãããŸãã
ç§ã¯ããã奜ãã§ã¯ãªããšèšã£ãã ãã®çç±ã¯ç°¡åã§ã-ååã«ããŒããããã·ã¹ãã ã§ã¯ã1ç§ã¯ç²ŸåºŠãäœãããŸãã ããã«ããã€ã¯ãç§åäœã®æ£ç¢ºãªããŒã¿ã¹ãã¬ãŒãžã«ã¯äœãããããŸãããã䟿å©ã§ãã äžæ¹ãããŒã¿ããã€ã¯ãç§ãŸã§ã®å Žåãå€ãç¬æã«è¡šç€ºãããããã«ããã«ã¯ã©ãããã°ããã§ããïŒ
ç°¡åã§ãïŒïŒSELECTã¯ãšãªã§ïŒé¢æ°to_charïŒïŒãŸãã¯date_truncã䜿çšããããtimestamptzïŒ0ïŒåã«ãã£ã¹ãããŸãïŒ
$ select now(), to_char(now(), 'YYYY-MM-DD HH24:MI:SS TZ'), date_trunc('second', now()), now()::timestamptz(0); now | to_char | date_trunc | now
2016幎7æã®PG Day'16 Russiaã«ã³ãã¡ã¬ã³ã¹ã§ãã¿ã€ã ã¹ã¿ã³ãã®æäœæ¹æ³ã«ã€ããŠè©³ããã話ãããŸãïŒ è³ªåãæºåããŠãã ãããç§ãã¡ã¯ãããã«çããããšããŸãã
Mail.ru Groupã®ååãæžããæçšãªèšäºãããããèŠã€ããã§ãããã timestamptzã ãã§ã¯ã¿ã¹ã¯ã解決ããã«ã¯äžååãªäŸãæç¢ºã«ç€ºããŠããŸãã