mirror of
https://we.phorge.it/source/phorge.git
synced 2024-11-27 17:22:42 +01:00
Merge a small amount of remaining "libphutil/" code with Phabricator, break libphutil dependency
Summary: Ref T13395. Moves a small amount of remaining "libphutil/" code into "phabricator/" and stops us from loading "libphutil/". Test Plan: Browsed around; there are likely remaining issues. Maniphest Tasks: T13395 Differential Revision: https://secure.phabricator.com/D20981
This commit is contained in:
parent
f9b3e3360b
commit
35a18146a2
14 changed files with 2831 additions and 14 deletions
769
externals/cldr/cldr_windows_timezones.xml
vendored
Normal file
769
externals/cldr/cldr_windows_timezones.xml
vendored
Normal file
|
@ -0,0 +1,769 @@
|
|||
<?xml version="1.0" encoding="UTF-8" ?>
|
||||
<!DOCTYPE supplementalData SYSTEM "../../common/dtd/ldmlSupplemental.dtd">
|
||||
<!--
|
||||
Copyright © 1991-2013 Unicode, Inc.
|
||||
CLDR data files are interpreted according to the LDML specification (http://unicode.org/reports/tr35/)
|
||||
For terms of use, see http://www.unicode.org/copyright.html
|
||||
-->
|
||||
|
||||
<supplementalData>
|
||||
<version number="$Revision$"/>
|
||||
<windowsZones>
|
||||
<mapTimezones otherVersion="7e00402" typeVersion="2016i">
|
||||
|
||||
<!-- (UTC-12:00) International Date Line West -->
|
||||
<mapZone other="Dateline Standard Time" territory="001" type="Etc/GMT+12"/>
|
||||
<mapZone other="Dateline Standard Time" territory="ZZ" type="Etc/GMT+12"/>
|
||||
|
||||
<!-- (UTC-11:00) Coordinated Universal Time-11 -->
|
||||
<mapZone other="UTC-11" territory="001" type="Etc/GMT+11"/>
|
||||
<mapZone other="UTC-11" territory="AS" type="Pacific/Pago_Pago"/>
|
||||
<mapZone other="UTC-11" territory="NU" type="Pacific/Niue"/>
|
||||
<mapZone other="UTC-11" territory="UM" type="Pacific/Midway"/>
|
||||
<mapZone other="UTC-11" territory="ZZ" type="Etc/GMT+11"/>
|
||||
|
||||
<!-- (UTC-10:00) Aleutian Islands -->
|
||||
<mapZone other="Aleutian Standard Time" territory="001" type="America/Adak"/>
|
||||
<mapZone other="Aleutian Standard Time" territory="US" type="America/Adak"/>
|
||||
|
||||
<!-- (UTC-10:00) Hawaii -->
|
||||
<mapZone other="Hawaiian Standard Time" territory="001" type="Pacific/Honolulu"/>
|
||||
<mapZone other="Hawaiian Standard Time" territory="CK" type="Pacific/Rarotonga"/>
|
||||
<mapZone other="Hawaiian Standard Time" territory="PF" type="Pacific/Tahiti"/>
|
||||
<mapZone other="Hawaiian Standard Time" territory="UM" type="Pacific/Johnston"/>
|
||||
<mapZone other="Hawaiian Standard Time" territory="US" type="Pacific/Honolulu"/>
|
||||
<mapZone other="Hawaiian Standard Time" territory="ZZ" type="Etc/GMT+10"/>
|
||||
|
||||
<!-- (UTC-09:30) Marquesas Islands -->
|
||||
<mapZone other="Marquesas Standard Time" territory="001" type="Pacific/Marquesas"/>
|
||||
<mapZone other="Marquesas Standard Time" territory="PF" type="Pacific/Marquesas"/>
|
||||
|
||||
<!-- (UTC-09:00) Alaska -->
|
||||
<mapZone other="Alaskan Standard Time" territory="001" type="America/Anchorage"/>
|
||||
<mapZone other="Alaskan Standard Time" territory="US" type="America/Anchorage America/Juneau America/Metlakatla America/Nome America/Sitka America/Yakutat"/>
|
||||
|
||||
<!-- (UTC-09:00) Coordinated Universal Time-09 -->
|
||||
<mapZone other="UTC-09" territory="001" type="Etc/GMT+9"/>
|
||||
<mapZone other="UTC-09" territory="PF" type="Pacific/Gambier"/>
|
||||
<mapZone other="UTC-09" territory="ZZ" type="Etc/GMT+9"/>
|
||||
|
||||
<!-- (UTC-08:00) Baja California -->
|
||||
<mapZone other="Pacific Standard Time (Mexico)" territory="001" type="America/Tijuana"/>
|
||||
<mapZone other="Pacific Standard Time (Mexico)" territory="MX" type="America/Tijuana America/Santa_Isabel"/>
|
||||
|
||||
<!-- (UTC-08:00) Coordinated Universal Time-08 -->
|
||||
<mapZone other="UTC-08" territory="001" type="Etc/GMT+8"/>
|
||||
<mapZone other="UTC-08" territory="PN" type="Pacific/Pitcairn"/>
|
||||
<mapZone other="UTC-08" territory="ZZ" type="Etc/GMT+8"/>
|
||||
|
||||
<!-- (UTC-08:00) Pacific Time (US & Canada) -->
|
||||
<mapZone other="Pacific Standard Time" territory="001" type="America/Los_Angeles"/>
|
||||
<mapZone other="Pacific Standard Time" territory="CA" type="America/Vancouver America/Dawson America/Whitehorse"/>
|
||||
<mapZone other="Pacific Standard Time" territory="US" type="America/Los_Angeles"/>
|
||||
<mapZone other="Pacific Standard Time" territory="ZZ" type="PST8PDT"/>
|
||||
|
||||
<!-- (UTC-07:00) Arizona -->
|
||||
<mapZone other="US Mountain Standard Time" territory="001" type="America/Phoenix"/>
|
||||
<mapZone other="US Mountain Standard Time" territory="CA" type="America/Dawson_Creek America/Creston America/Fort_Nelson"/>
|
||||
<mapZone other="US Mountain Standard Time" territory="MX" type="America/Hermosillo"/>
|
||||
<mapZone other="US Mountain Standard Time" territory="US" type="America/Phoenix"/>
|
||||
<mapZone other="US Mountain Standard Time" territory="ZZ" type="Etc/GMT+7"/>
|
||||
|
||||
<!-- (UTC-07:00) Chihuahua, La Paz, Mazatlan -->
|
||||
<mapZone other="Mountain Standard Time (Mexico)" territory="001" type="America/Chihuahua"/>
|
||||
<mapZone other="Mountain Standard Time (Mexico)" territory="MX" type="America/Chihuahua America/Mazatlan"/>
|
||||
|
||||
<!-- (UTC-07:00) Mountain Time (US & Canada) -->
|
||||
<mapZone other="Mountain Standard Time" territory="001" type="America/Denver"/>
|
||||
<mapZone other="Mountain Standard Time" territory="CA" type="America/Edmonton America/Cambridge_Bay America/Inuvik America/Yellowknife"/>
|
||||
<mapZone other="Mountain Standard Time" territory="MX" type="America/Ojinaga"/>
|
||||
<mapZone other="Mountain Standard Time" territory="US" type="America/Denver America/Boise"/>
|
||||
<mapZone other="Mountain Standard Time" territory="ZZ" type="MST7MDT"/>
|
||||
|
||||
<!-- (UTC-06:00) Central America -->
|
||||
<mapZone other="Central America Standard Time" territory="001" type="America/Guatemala"/>
|
||||
<mapZone other="Central America Standard Time" territory="BZ" type="America/Belize"/>
|
||||
<mapZone other="Central America Standard Time" territory="CR" type="America/Costa_Rica"/>
|
||||
<mapZone other="Central America Standard Time" territory="EC" type="Pacific/Galapagos"/>
|
||||
<mapZone other="Central America Standard Time" territory="GT" type="America/Guatemala"/>
|
||||
<mapZone other="Central America Standard Time" territory="HN" type="America/Tegucigalpa"/>
|
||||
<mapZone other="Central America Standard Time" territory="NI" type="America/Managua"/>
|
||||
<mapZone other="Central America Standard Time" territory="SV" type="America/El_Salvador"/>
|
||||
<mapZone other="Central America Standard Time" territory="ZZ" type="Etc/GMT+6"/>
|
||||
|
||||
<!-- (UTC-06:00) Central Time (US & Canada) -->
|
||||
<mapZone other="Central Standard Time" territory="001" type="America/Chicago"/>
|
||||
<mapZone other="Central Standard Time" territory="CA" type="America/Winnipeg America/Rainy_River America/Rankin_Inlet America/Resolute"/>
|
||||
<mapZone other="Central Standard Time" territory="MX" type="America/Matamoros"/>
|
||||
<mapZone other="Central Standard Time" territory="US" type="America/Chicago America/Indiana/Knox America/Indiana/Tell_City America/Menominee America/North_Dakota/Beulah America/North_Dakota/Center America/North_Dakota/New_Salem"/>
|
||||
<mapZone other="Central Standard Time" territory="ZZ" type="CST6CDT"/>
|
||||
|
||||
<!-- (UTC-06:00) Easter Island -->
|
||||
<mapZone other="Easter Island Standard Time" territory="001" type="Pacific/Easter"/>
|
||||
<mapZone other="Easter Island Standard Time" territory="CL" type="Pacific/Easter"/>
|
||||
|
||||
<!-- (UTC-06:00) Guadalajara, Mexico City, Monterrey -->
|
||||
<mapZone other="Central Standard Time (Mexico)" territory="001" type="America/Mexico_City"/>
|
||||
<mapZone other="Central Standard Time (Mexico)" territory="MX" type="America/Mexico_City America/Bahia_Banderas America/Merida America/Monterrey"/>
|
||||
|
||||
<!-- (UTC-06:00) Saskatchewan -->
|
||||
<mapZone other="Canada Central Standard Time" territory="001" type="America/Regina"/>
|
||||
<mapZone other="Canada Central Standard Time" territory="CA" type="America/Regina America/Swift_Current"/>
|
||||
|
||||
<!-- (UTC-05:00) Bogota, Lima, Quito, Rio Branco -->
|
||||
<mapZone other="SA Pacific Standard Time" territory="001" type="America/Bogota"/>
|
||||
<mapZone other="SA Pacific Standard Time" territory="BR" type="America/Rio_Branco America/Eirunepe"/>
|
||||
<mapZone other="SA Pacific Standard Time" territory="CA" type="America/Coral_Harbour"/>
|
||||
<mapZone other="SA Pacific Standard Time" territory="CO" type="America/Bogota"/>
|
||||
<mapZone other="SA Pacific Standard Time" territory="EC" type="America/Guayaquil"/>
|
||||
<mapZone other="SA Pacific Standard Time" territory="JM" type="America/Jamaica"/>
|
||||
<mapZone other="SA Pacific Standard Time" territory="KY" type="America/Cayman"/>
|
||||
<mapZone other="SA Pacific Standard Time" territory="PA" type="America/Panama"/>
|
||||
<mapZone other="SA Pacific Standard Time" territory="PE" type="America/Lima"/>
|
||||
<mapZone other="SA Pacific Standard Time" territory="ZZ" type="Etc/GMT+5"/>
|
||||
|
||||
<!-- (UTC-05:00) Chetumal -->
|
||||
<mapZone other="Eastern Standard Time (Mexico)" territory="001" type="America/Cancun"/>
|
||||
<mapZone other="Eastern Standard Time (Mexico)" territory="MX" type="America/Cancun"/>
|
||||
|
||||
<!-- (UTC-05:00) Eastern Time (US & Canada) -->
|
||||
<mapZone other="Eastern Standard Time" territory="001" type="America/New_York"/>
|
||||
<mapZone other="Eastern Standard Time" territory="BS" type="America/Nassau"/>
|
||||
<mapZone other="Eastern Standard Time" territory="CA" type="America/Toronto America/Iqaluit America/Montreal America/Nipigon America/Pangnirtung America/Thunder_Bay"/>
|
||||
<mapZone other="Eastern Standard Time" territory="US" type="America/New_York America/Detroit America/Indiana/Petersburg America/Indiana/Vincennes America/Indiana/Winamac America/Kentucky/Monticello America/Louisville"/>
|
||||
<mapZone other="Eastern Standard Time" territory="ZZ" type="EST5EDT"/>
|
||||
|
||||
<!-- (UTC-05:00) Haiti -->
|
||||
<mapZone other="Haiti Standard Time" territory="001" type="America/Port-au-Prince"/>
|
||||
<mapZone other="Haiti Standard Time" territory="HT" type="America/Port-au-Prince"/>
|
||||
|
||||
<!-- (UTC-05:00) Havana -->
|
||||
<mapZone other="Cuba Standard Time" territory="001" type="America/Havana"/>
|
||||
<mapZone other="Cuba Standard Time" territory="CU" type="America/Havana"/>
|
||||
|
||||
<!-- (UTC-05:00) Indiana (East) -->
|
||||
<mapZone other="US Eastern Standard Time" territory="001" type="America/Indianapolis"/>
|
||||
<mapZone other="US Eastern Standard Time" territory="US" type="America/Indianapolis America/Indiana/Marengo America/Indiana/Vevay"/>
|
||||
|
||||
<!-- (UTC-04:00) Asuncion -->
|
||||
<mapZone other="Paraguay Standard Time" territory="001" type="America/Asuncion"/>
|
||||
<mapZone other="Paraguay Standard Time" territory="PY" type="America/Asuncion"/>
|
||||
|
||||
<!-- (UTC-04:00) Atlantic Time (Canada) -->
|
||||
<mapZone other="Atlantic Standard Time" territory="001" type="America/Halifax"/>
|
||||
<mapZone other="Atlantic Standard Time" territory="BM" type="Atlantic/Bermuda"/>
|
||||
<mapZone other="Atlantic Standard Time" territory="CA" type="America/Halifax America/Glace_Bay America/Goose_Bay America/Moncton"/>
|
||||
<mapZone other="Atlantic Standard Time" territory="GL" type="America/Thule"/>
|
||||
|
||||
<!-- (UTC-04:00) Caracas -->
|
||||
<mapZone other="Venezuela Standard Time" territory="001" type="America/Caracas"/>
|
||||
<mapZone other="Venezuela Standard Time" territory="VE" type="America/Caracas"/>
|
||||
|
||||
<!-- (UTC-04:00) Cuiaba -->
|
||||
<mapZone other="Central Brazilian Standard Time" territory="001" type="America/Cuiaba"/>
|
||||
<mapZone other="Central Brazilian Standard Time" territory="BR" type="America/Cuiaba America/Campo_Grande"/>
|
||||
|
||||
<!-- (UTC-04:00) Georgetown, La Paz, Manaus, San Juan -->
|
||||
<mapZone other="SA Western Standard Time" territory="001" type="America/La_Paz"/>
|
||||
<mapZone other="SA Western Standard Time" territory="AG" type="America/Antigua"/>
|
||||
<mapZone other="SA Western Standard Time" territory="AI" type="America/Anguilla"/>
|
||||
<mapZone other="SA Western Standard Time" territory="AW" type="America/Aruba"/>
|
||||
<mapZone other="SA Western Standard Time" territory="BB" type="America/Barbados"/>
|
||||
<mapZone other="SA Western Standard Time" territory="BL" type="America/St_Barthelemy"/>
|
||||
<mapZone other="SA Western Standard Time" territory="BO" type="America/La_Paz"/>
|
||||
<mapZone other="SA Western Standard Time" territory="BQ" type="America/Kralendijk"/>
|
||||
<mapZone other="SA Western Standard Time" territory="BR" type="America/Manaus America/Boa_Vista America/Porto_Velho"/>
|
||||
<mapZone other="SA Western Standard Time" territory="CA" type="America/Blanc-Sablon"/>
|
||||
<mapZone other="SA Western Standard Time" territory="CW" type="America/Curacao"/>
|
||||
<mapZone other="SA Western Standard Time" territory="DM" type="America/Dominica"/>
|
||||
<mapZone other="SA Western Standard Time" territory="DO" type="America/Santo_Domingo"/>
|
||||
<mapZone other="SA Western Standard Time" territory="GD" type="America/Grenada"/>
|
||||
<mapZone other="SA Western Standard Time" territory="GP" type="America/Guadeloupe"/>
|
||||
<mapZone other="SA Western Standard Time" territory="GY" type="America/Guyana"/>
|
||||
<mapZone other="SA Western Standard Time" territory="KN" type="America/St_Kitts"/>
|
||||
<mapZone other="SA Western Standard Time" territory="LC" type="America/St_Lucia"/>
|
||||
<mapZone other="SA Western Standard Time" territory="MF" type="America/Marigot"/>
|
||||
<mapZone other="SA Western Standard Time" territory="MQ" type="America/Martinique"/>
|
||||
<mapZone other="SA Western Standard Time" territory="MS" type="America/Montserrat"/>
|
||||
<mapZone other="SA Western Standard Time" territory="PR" type="America/Puerto_Rico"/>
|
||||
<mapZone other="SA Western Standard Time" territory="SX" type="America/Lower_Princes"/>
|
||||
<mapZone other="SA Western Standard Time" territory="TT" type="America/Port_of_Spain"/>
|
||||
<mapZone other="SA Western Standard Time" territory="VC" type="America/St_Vincent"/>
|
||||
<mapZone other="SA Western Standard Time" territory="VG" type="America/Tortola"/>
|
||||
<mapZone other="SA Western Standard Time" territory="VI" type="America/St_Thomas"/>
|
||||
<mapZone other="SA Western Standard Time" territory="ZZ" type="Etc/GMT+4"/>
|
||||
|
||||
<!-- (UTC-04:00) Santiago -->
|
||||
<mapZone other="Pacific SA Standard Time" territory="001" type="America/Santiago"/>
|
||||
<mapZone other="Pacific SA Standard Time" territory="AQ" type="Antarctica/Palmer"/>
|
||||
<mapZone other="Pacific SA Standard Time" territory="CL" type="America/Santiago"/>
|
||||
|
||||
<!-- (UTC-04:00) Turks and Caicos -->
|
||||
<mapZone other="Turks And Caicos Standard Time" territory="001" type="America/Grand_Turk"/>
|
||||
<mapZone other="Turks And Caicos Standard Time" territory="TC" type="America/Grand_Turk"/>
|
||||
|
||||
<!-- (UTC-03:30) Newfoundland -->
|
||||
<mapZone other="Newfoundland Standard Time" territory="001" type="America/St_Johns"/>
|
||||
<mapZone other="Newfoundland Standard Time" territory="CA" type="America/St_Johns"/>
|
||||
|
||||
<!-- (UTC-03:00) Araguaina -->
|
||||
<mapZone other="Tocantins Standard Time" territory="001" type="America/Araguaina"/>
|
||||
<mapZone other="Tocantins Standard Time" territory="BR" type="America/Araguaina"/>
|
||||
|
||||
<!-- (UTC-03:00) Brasilia -->
|
||||
<mapZone other="E. South America Standard Time" territory="001" type="America/Sao_Paulo"/>
|
||||
<mapZone other="E. South America Standard Time" territory="BR" type="America/Sao_Paulo"/>
|
||||
|
||||
<!-- (UTC-03:00) Cayenne, Fortaleza -->
|
||||
<mapZone other="SA Eastern Standard Time" territory="001" type="America/Cayenne"/>
|
||||
<mapZone other="SA Eastern Standard Time" territory="AQ" type="Antarctica/Rothera"/>
|
||||
<mapZone other="SA Eastern Standard Time" territory="BR" type="America/Fortaleza America/Belem America/Maceio America/Recife America/Santarem"/>
|
||||
<mapZone other="SA Eastern Standard Time" territory="FK" type="Atlantic/Stanley"/>
|
||||
<mapZone other="SA Eastern Standard Time" territory="GF" type="America/Cayenne"/>
|
||||
<mapZone other="SA Eastern Standard Time" territory="SR" type="America/Paramaribo"/>
|
||||
<mapZone other="SA Eastern Standard Time" territory="ZZ" type="Etc/GMT+3"/>
|
||||
|
||||
<!-- (UTC-03:00) City of Buenos Aires -->
|
||||
<mapZone other="Argentina Standard Time" territory="001" type="America/Buenos_Aires"/>
|
||||
<mapZone other="Argentina Standard Time" territory="AR" type="America/Buenos_Aires America/Argentina/La_Rioja America/Argentina/Rio_Gallegos America/Argentina/Salta America/Argentina/San_Juan America/Argentina/San_Luis America/Argentina/Tucuman America/Argentina/Ushuaia America/Catamarca America/Cordoba America/Jujuy America/Mendoza"/>
|
||||
|
||||
<!-- (UTC-03:00) Greenland -->
|
||||
<mapZone other="Greenland Standard Time" territory="001" type="America/Godthab"/>
|
||||
<mapZone other="Greenland Standard Time" territory="GL" type="America/Godthab"/>
|
||||
|
||||
<!-- (UTC-03:00) Montevideo -->
|
||||
<mapZone other="Montevideo Standard Time" territory="001" type="America/Montevideo"/>
|
||||
<mapZone other="Montevideo Standard Time" territory="UY" type="America/Montevideo"/>
|
||||
|
||||
<!-- (UTC-03:00) Saint Pierre and Miquelon -->
|
||||
<mapZone other="Saint Pierre Standard Time" territory="001" type="America/Miquelon"/>
|
||||
<mapZone other="Saint Pierre Standard Time" territory="PM" type="America/Miquelon"/>
|
||||
|
||||
<!-- (UTC-03:00) Salvador -->
|
||||
<mapZone other="Bahia Standard Time" territory="001" type="America/Bahia"/>
|
||||
<mapZone other="Bahia Standard Time" territory="BR" type="America/Bahia"/>
|
||||
|
||||
<!-- (UTC-02:00) Coordinated Universal Time-02 -->
|
||||
<mapZone other="UTC-02" territory="001" type="Etc/GMT+2"/>
|
||||
<mapZone other="UTC-02" territory="BR" type="America/Noronha"/>
|
||||
<mapZone other="UTC-02" territory="GS" type="Atlantic/South_Georgia"/>
|
||||
<mapZone other="UTC-02" territory="ZZ" type="Etc/GMT+2"/>
|
||||
|
||||
<!-- (UTC-01:00) Azores -->
|
||||
<mapZone other="Azores Standard Time" territory="001" type="Atlantic/Azores"/>
|
||||
<mapZone other="Azores Standard Time" territory="GL" type="America/Scoresbysund"/>
|
||||
<mapZone other="Azores Standard Time" territory="PT" type="Atlantic/Azores"/>
|
||||
|
||||
<!-- (UTC-01:00) Cabo Verde Is. -->
|
||||
<mapZone other="Cape Verde Standard Time" territory="001" type="Atlantic/Cape_Verde"/>
|
||||
<mapZone other="Cape Verde Standard Time" territory="CV" type="Atlantic/Cape_Verde"/>
|
||||
<mapZone other="Cape Verde Standard Time" territory="ZZ" type="Etc/GMT+1"/>
|
||||
|
||||
<!-- (UTC) Coordinated Universal Time -->
|
||||
<mapZone other="UTC" territory="001" type="Etc/GMT"/>
|
||||
<mapZone other="UTC" territory="GL" type="America/Danmarkshavn"/>
|
||||
<mapZone other="UTC" territory="ZZ" type="Etc/GMT"/>
|
||||
|
||||
<!-- (UTC+00:00) Casablanca -->
|
||||
<mapZone other="Morocco Standard Time" territory="001" type="Africa/Casablanca"/>
|
||||
<mapZone other="Morocco Standard Time" territory="EH" type="Africa/El_Aaiun"/>
|
||||
<mapZone other="Morocco Standard Time" territory="MA" type="Africa/Casablanca"/>
|
||||
|
||||
<!-- (UTC+00:00) Dublin, Edinburgh, Lisbon, London -->
|
||||
<mapZone other="GMT Standard Time" territory="001" type="Europe/London"/>
|
||||
<mapZone other="GMT Standard Time" territory="ES" type="Atlantic/Canary"/>
|
||||
<mapZone other="GMT Standard Time" territory="FO" type="Atlantic/Faeroe"/>
|
||||
<mapZone other="GMT Standard Time" territory="GB" type="Europe/London"/>
|
||||
<mapZone other="GMT Standard Time" territory="GG" type="Europe/Guernsey"/>
|
||||
<mapZone other="GMT Standard Time" territory="IE" type="Europe/Dublin"/>
|
||||
<mapZone other="GMT Standard Time" territory="IM" type="Europe/Isle_of_Man"/>
|
||||
<mapZone other="GMT Standard Time" territory="JE" type="Europe/Jersey"/>
|
||||
<mapZone other="GMT Standard Time" territory="PT" type="Europe/Lisbon Atlantic/Madeira"/>
|
||||
|
||||
<!-- (UTC+00:00) Monrovia, Reykjavik -->
|
||||
<mapZone other="Greenwich Standard Time" territory="001" type="Atlantic/Reykjavik"/>
|
||||
<mapZone other="Greenwich Standard Time" territory="BF" type="Africa/Ouagadougou"/>
|
||||
<mapZone other="Greenwich Standard Time" territory="CI" type="Africa/Abidjan"/>
|
||||
<mapZone other="Greenwich Standard Time" territory="GH" type="Africa/Accra"/>
|
||||
<mapZone other="Greenwich Standard Time" territory="GM" type="Africa/Banjul"/>
|
||||
<mapZone other="Greenwich Standard Time" territory="GN" type="Africa/Conakry"/>
|
||||
<mapZone other="Greenwich Standard Time" territory="GW" type="Africa/Bissau"/>
|
||||
<mapZone other="Greenwich Standard Time" territory="IS" type="Atlantic/Reykjavik"/>
|
||||
<mapZone other="Greenwich Standard Time" territory="LR" type="Africa/Monrovia"/>
|
||||
<mapZone other="Greenwich Standard Time" territory="ML" type="Africa/Bamako"/>
|
||||
<mapZone other="Greenwich Standard Time" territory="MR" type="Africa/Nouakchott"/>
|
||||
<mapZone other="Greenwich Standard Time" territory="SH" type="Atlantic/St_Helena"/>
|
||||
<mapZone other="Greenwich Standard Time" territory="SL" type="Africa/Freetown"/>
|
||||
<mapZone other="Greenwich Standard Time" territory="SN" type="Africa/Dakar"/>
|
||||
<mapZone other="Greenwich Standard Time" territory="ST" type="Africa/Sao_Tome"/>
|
||||
<mapZone other="Greenwich Standard Time" territory="TG" type="Africa/Lome"/>
|
||||
|
||||
<!-- (UTC+01:00) Amsterdam, Berlin, Bern, Rome, Stockholm, Vienna -->
|
||||
<mapZone other="W. Europe Standard Time" territory="001" type="Europe/Berlin"/>
|
||||
<mapZone other="W. Europe Standard Time" territory="AD" type="Europe/Andorra"/>
|
||||
<mapZone other="W. Europe Standard Time" territory="AT" type="Europe/Vienna"/>
|
||||
<mapZone other="W. Europe Standard Time" territory="CH" type="Europe/Zurich"/>
|
||||
<mapZone other="W. Europe Standard Time" territory="DE" type="Europe/Berlin Europe/Busingen"/>
|
||||
<mapZone other="W. Europe Standard Time" territory="GI" type="Europe/Gibraltar"/>
|
||||
<mapZone other="W. Europe Standard Time" territory="IT" type="Europe/Rome"/>
|
||||
<mapZone other="W. Europe Standard Time" territory="LI" type="Europe/Vaduz"/>
|
||||
<mapZone other="W. Europe Standard Time" territory="LU" type="Europe/Luxembourg"/>
|
||||
<mapZone other="W. Europe Standard Time" territory="MC" type="Europe/Monaco"/>
|
||||
<mapZone other="W. Europe Standard Time" territory="MT" type="Europe/Malta"/>
|
||||
<mapZone other="W. Europe Standard Time" territory="NL" type="Europe/Amsterdam"/>
|
||||
<mapZone other="W. Europe Standard Time" territory="NO" type="Europe/Oslo"/>
|
||||
<mapZone other="W. Europe Standard Time" territory="SE" type="Europe/Stockholm"/>
|
||||
<mapZone other="W. Europe Standard Time" territory="SJ" type="Arctic/Longyearbyen"/>
|
||||
<mapZone other="W. Europe Standard Time" territory="SM" type="Europe/San_Marino"/>
|
||||
<mapZone other="W. Europe Standard Time" territory="VA" type="Europe/Vatican"/>
|
||||
|
||||
<!-- (UTC+01:00) Belgrade, Bratislava, Budapest, Ljubljana, Prague -->
|
||||
<mapZone other="Central Europe Standard Time" territory="001" type="Europe/Budapest"/>
|
||||
<mapZone other="Central Europe Standard Time" territory="AL" type="Europe/Tirane"/>
|
||||
<mapZone other="Central Europe Standard Time" territory="CZ" type="Europe/Prague"/>
|
||||
<mapZone other="Central Europe Standard Time" territory="HU" type="Europe/Budapest"/>
|
||||
<mapZone other="Central Europe Standard Time" territory="ME" type="Europe/Podgorica"/>
|
||||
<mapZone other="Central Europe Standard Time" territory="RS" type="Europe/Belgrade"/>
|
||||
<mapZone other="Central Europe Standard Time" territory="SI" type="Europe/Ljubljana"/>
|
||||
<mapZone other="Central Europe Standard Time" territory="SK" type="Europe/Bratislava"/>
|
||||
|
||||
<!-- (UTC+01:00) Brussels, Copenhagen, Madrid, Paris -->
|
||||
<mapZone other="Romance Standard Time" territory="001" type="Europe/Paris"/>
|
||||
<mapZone other="Romance Standard Time" territory="BE" type="Europe/Brussels"/>
|
||||
<mapZone other="Romance Standard Time" territory="DK" type="Europe/Copenhagen"/>
|
||||
<mapZone other="Romance Standard Time" territory="ES" type="Europe/Madrid Africa/Ceuta"/>
|
||||
<mapZone other="Romance Standard Time" territory="FR" type="Europe/Paris"/>
|
||||
|
||||
<!-- (UTC+01:00) Sarajevo, Skopje, Warsaw, Zagreb -->
|
||||
<mapZone other="Central European Standard Time" territory="001" type="Europe/Warsaw"/>
|
||||
<mapZone other="Central European Standard Time" territory="BA" type="Europe/Sarajevo"/>
|
||||
<mapZone other="Central European Standard Time" territory="HR" type="Europe/Zagreb"/>
|
||||
<mapZone other="Central European Standard Time" territory="MK" type="Europe/Skopje"/>
|
||||
<mapZone other="Central European Standard Time" territory="PL" type="Europe/Warsaw"/>
|
||||
|
||||
<!-- (UTC+01:00) West Central Africa -->
|
||||
<mapZone other="W. Central Africa Standard Time" territory="001" type="Africa/Lagos"/>
|
||||
<mapZone other="W. Central Africa Standard Time" territory="AO" type="Africa/Luanda"/>
|
||||
<mapZone other="W. Central Africa Standard Time" territory="BJ" type="Africa/Porto-Novo"/>
|
||||
<mapZone other="W. Central Africa Standard Time" territory="CD" type="Africa/Kinshasa"/>
|
||||
<mapZone other="W. Central Africa Standard Time" territory="CF" type="Africa/Bangui"/>
|
||||
<mapZone other="W. Central Africa Standard Time" territory="CG" type="Africa/Brazzaville"/>
|
||||
<mapZone other="W. Central Africa Standard Time" territory="CM" type="Africa/Douala"/>
|
||||
<mapZone other="W. Central Africa Standard Time" territory="DZ" type="Africa/Algiers"/>
|
||||
<mapZone other="W. Central Africa Standard Time" territory="GA" type="Africa/Libreville"/>
|
||||
<mapZone other="W. Central Africa Standard Time" territory="GQ" type="Africa/Malabo"/>
|
||||
<mapZone other="W. Central Africa Standard Time" territory="NE" type="Africa/Niamey"/>
|
||||
<mapZone other="W. Central Africa Standard Time" territory="NG" type="Africa/Lagos"/>
|
||||
<mapZone other="W. Central Africa Standard Time" territory="TD" type="Africa/Ndjamena"/>
|
||||
<mapZone other="W. Central Africa Standard Time" territory="TN" type="Africa/Tunis"/>
|
||||
<mapZone other="W. Central Africa Standard Time" territory="ZZ" type="Etc/GMT-1"/>
|
||||
|
||||
<!-- (UTC+01:00) Windhoek -->
|
||||
<mapZone other="Namibia Standard Time" territory="001" type="Africa/Windhoek"/>
|
||||
<mapZone other="Namibia Standard Time" territory="NA" type="Africa/Windhoek"/>
|
||||
|
||||
<!-- (UTC+02:00) Amman -->
|
||||
<mapZone other="Jordan Standard Time" territory="001" type="Asia/Amman"/>
|
||||
<mapZone other="Jordan Standard Time" territory="JO" type="Asia/Amman"/>
|
||||
|
||||
<!-- (UTC+02:00) Athens, Bucharest -->
|
||||
<mapZone other="GTB Standard Time" territory="001" type="Europe/Bucharest"/>
|
||||
<mapZone other="GTB Standard Time" territory="CY" type="Asia/Nicosia"/>
|
||||
<mapZone other="GTB Standard Time" territory="GR" type="Europe/Athens"/>
|
||||
<mapZone other="GTB Standard Time" territory="RO" type="Europe/Bucharest"/>
|
||||
|
||||
<!-- (UTC+02:00) Beirut -->
|
||||
<mapZone other="Middle East Standard Time" territory="001" type="Asia/Beirut"/>
|
||||
<mapZone other="Middle East Standard Time" territory="LB" type="Asia/Beirut"/>
|
||||
|
||||
<!-- (UTC+02:00) Cairo -->
|
||||
<mapZone other="Egypt Standard Time" territory="001" type="Africa/Cairo"/>
|
||||
<mapZone other="Egypt Standard Time" territory="EG" type="Africa/Cairo"/>
|
||||
|
||||
<!-- (UTC+02:00) Chisinau -->
|
||||
<mapZone other="E. Europe Standard Time" territory="001" type="Europe/Chisinau"/>
|
||||
<mapZone other="E. Europe Standard Time" territory="MD" type="Europe/Chisinau"/>
|
||||
|
||||
<!-- (UTC+02:00) Damascus -->
|
||||
<mapZone other="Syria Standard Time" territory="001" type="Asia/Damascus"/>
|
||||
<mapZone other="Syria Standard Time" territory="SY" type="Asia/Damascus"/>
|
||||
|
||||
<!-- (UTC+02:00) Gaza, Hebron -->
|
||||
<mapZone other="West Bank Standard Time" territory="001" type="Asia/Hebron"/>
|
||||
<mapZone other="West Bank Standard Time" territory="PS" type="Asia/Hebron Asia/Gaza"/>
|
||||
|
||||
<!-- (UTC+02:00) Harare, Pretoria -->
|
||||
<mapZone other="South Africa Standard Time" territory="001" type="Africa/Johannesburg"/>
|
||||
<mapZone other="South Africa Standard Time" territory="BI" type="Africa/Bujumbura"/>
|
||||
<mapZone other="South Africa Standard Time" territory="BW" type="Africa/Gaborone"/>
|
||||
<mapZone other="South Africa Standard Time" territory="CD" type="Africa/Lubumbashi"/>
|
||||
<mapZone other="South Africa Standard Time" territory="LS" type="Africa/Maseru"/>
|
||||
<mapZone other="South Africa Standard Time" territory="MW" type="Africa/Blantyre"/>
|
||||
<mapZone other="South Africa Standard Time" territory="MZ" type="Africa/Maputo"/>
|
||||
<mapZone other="South Africa Standard Time" territory="RW" type="Africa/Kigali"/>
|
||||
<mapZone other="South Africa Standard Time" territory="SZ" type="Africa/Mbabane"/>
|
||||
<mapZone other="South Africa Standard Time" territory="ZA" type="Africa/Johannesburg"/>
|
||||
<mapZone other="South Africa Standard Time" territory="ZM" type="Africa/Lusaka"/>
|
||||
<mapZone other="South Africa Standard Time" territory="ZW" type="Africa/Harare"/>
|
||||
<mapZone other="South Africa Standard Time" territory="ZZ" type="Etc/GMT-2"/>
|
||||
|
||||
<!-- (UTC+02:00) Helsinki, Kyiv, Riga, Sofia, Tallinn, Vilnius -->
|
||||
<mapZone other="FLE Standard Time" territory="001" type="Europe/Kiev"/>
|
||||
<mapZone other="FLE Standard Time" territory="AX" type="Europe/Mariehamn"/>
|
||||
<mapZone other="FLE Standard Time" territory="BG" type="Europe/Sofia"/>
|
||||
<mapZone other="FLE Standard Time" territory="EE" type="Europe/Tallinn"/>
|
||||
<mapZone other="FLE Standard Time" territory="FI" type="Europe/Helsinki"/>
|
||||
<mapZone other="FLE Standard Time" territory="LT" type="Europe/Vilnius"/>
|
||||
<mapZone other="FLE Standard Time" territory="LV" type="Europe/Riga"/>
|
||||
<mapZone other="FLE Standard Time" territory="UA" type="Europe/Kiev Europe/Uzhgorod Europe/Zaporozhye"/>
|
||||
|
||||
<!-- (UTC+02:00) Istanbul -->
|
||||
<mapZone other="Turkey Standard Time" territory="001" type="Europe/Istanbul"/>
|
||||
<mapZone other="Turkey Standard Time" territory="TR" type="Europe/Istanbul"/>
|
||||
|
||||
<!-- (UTC+02:00) Jerusalem -->
|
||||
<mapZone other="Israel Standard Time" territory="001" type="Asia/Jerusalem"/>
|
||||
<mapZone other="Israel Standard Time" territory="IL" type="Asia/Jerusalem"/>
|
||||
|
||||
<!-- (UTC+02:00) Kaliningrad -->
|
||||
<mapZone other="Kaliningrad Standard Time" territory="001" type="Europe/Kaliningrad"/>
|
||||
<mapZone other="Kaliningrad Standard Time" territory="RU" type="Europe/Kaliningrad"/>
|
||||
|
||||
<!-- (UTC+02:00) Tripoli -->
|
||||
<mapZone other="Libya Standard Time" territory="001" type="Africa/Tripoli"/>
|
||||
<mapZone other="Libya Standard Time" territory="LY" type="Africa/Tripoli"/>
|
||||
|
||||
<!-- (UTC+03:00) Baghdad -->
|
||||
<mapZone other="Arabic Standard Time" territory="001" type="Asia/Baghdad"/>
|
||||
<mapZone other="Arabic Standard Time" territory="IQ" type="Asia/Baghdad"/>
|
||||
|
||||
<!-- (UTC+03:00) Kuwait, Riyadh -->
|
||||
<mapZone other="Arab Standard Time" territory="001" type="Asia/Riyadh"/>
|
||||
<mapZone other="Arab Standard Time" territory="BH" type="Asia/Bahrain"/>
|
||||
<mapZone other="Arab Standard Time" territory="KW" type="Asia/Kuwait"/>
|
||||
<mapZone other="Arab Standard Time" territory="QA" type="Asia/Qatar"/>
|
||||
<mapZone other="Arab Standard Time" territory="SA" type="Asia/Riyadh"/>
|
||||
<mapZone other="Arab Standard Time" territory="YE" type="Asia/Aden"/>
|
||||
|
||||
<!-- (UTC+03:00) Minsk -->
|
||||
<mapZone other="Belarus Standard Time" territory="001" type="Europe/Minsk"/>
|
||||
<mapZone other="Belarus Standard Time" territory="BY" type="Europe/Minsk"/>
|
||||
|
||||
<!-- (UTC+03:00) Moscow, St. Petersburg, Volgograd -->
|
||||
<mapZone other="Russian Standard Time" territory="001" type="Europe/Moscow"/>
|
||||
<mapZone other="Russian Standard Time" territory="RU" type="Europe/Moscow Europe/Kirov Europe/Volgograd"/>
|
||||
<mapZone other="Russian Standard Time" territory="UA" type="Europe/Simferopol"/>
|
||||
|
||||
<!-- (UTC+03:00) Nairobi -->
|
||||
<mapZone other="E. Africa Standard Time" territory="001" type="Africa/Nairobi"/>
|
||||
<mapZone other="E. Africa Standard Time" territory="AQ" type="Antarctica/Syowa"/>
|
||||
<mapZone other="E. Africa Standard Time" territory="DJ" type="Africa/Djibouti"/>
|
||||
<mapZone other="E. Africa Standard Time" territory="ER" type="Africa/Asmera"/>
|
||||
<mapZone other="E. Africa Standard Time" territory="ET" type="Africa/Addis_Ababa"/>
|
||||
<mapZone other="E. Africa Standard Time" territory="KE" type="Africa/Nairobi"/>
|
||||
<mapZone other="E. Africa Standard Time" territory="KM" type="Indian/Comoro"/>
|
||||
<mapZone other="E. Africa Standard Time" territory="MG" type="Indian/Antananarivo"/>
|
||||
<mapZone other="E. Africa Standard Time" territory="SD" type="Africa/Khartoum"/>
|
||||
<mapZone other="E. Africa Standard Time" territory="SO" type="Africa/Mogadishu"/>
|
||||
<mapZone other="E. Africa Standard Time" territory="SS" type="Africa/Juba"/>
|
||||
<mapZone other="E. Africa Standard Time" territory="TZ" type="Africa/Dar_es_Salaam"/>
|
||||
<mapZone other="E. Africa Standard Time" territory="UG" type="Africa/Kampala"/>
|
||||
<mapZone other="E. Africa Standard Time" territory="YT" type="Indian/Mayotte"/>
|
||||
<mapZone other="E. Africa Standard Time" territory="ZZ" type="Etc/GMT-3"/>
|
||||
|
||||
<!-- (UTC+03:30) Tehran -->
|
||||
<mapZone other="Iran Standard Time" territory="001" type="Asia/Tehran"/>
|
||||
<mapZone other="Iran Standard Time" territory="IR" type="Asia/Tehran"/>
|
||||
|
||||
<!-- (UTC+04:00) Abu Dhabi, Muscat -->
|
||||
<mapZone other="Arabian Standard Time" territory="001" type="Asia/Dubai"/>
|
||||
<mapZone other="Arabian Standard Time" territory="AE" type="Asia/Dubai"/>
|
||||
<mapZone other="Arabian Standard Time" territory="OM" type="Asia/Muscat"/>
|
||||
<mapZone other="Arabian Standard Time" territory="ZZ" type="Etc/GMT-4"/>
|
||||
|
||||
<!-- (UTC+04:00) Astrakhan, Ulyanovsk -->
|
||||
<mapZone other="Astrakhan Standard Time" territory="001" type="Europe/Astrakhan"/>
|
||||
<mapZone other="Astrakhan Standard Time" territory="RU" type="Europe/Astrakhan Europe/Ulyanovsk"/>
|
||||
|
||||
<!-- (UTC+04:00) Baku -->
|
||||
<mapZone other="Azerbaijan Standard Time" territory="001" type="Asia/Baku"/>
|
||||
<mapZone other="Azerbaijan Standard Time" territory="AZ" type="Asia/Baku"/>
|
||||
|
||||
<!-- (UTC+04:00) Izhevsk, Samara -->
|
||||
<mapZone other="Russia Time Zone 3" territory="001" type="Europe/Samara"/>
|
||||
<mapZone other="Russia Time Zone 3" territory="RU" type="Europe/Samara"/>
|
||||
|
||||
<!-- (UTC+04:00) Port Louis -->
|
||||
<mapZone other="Mauritius Standard Time" territory="001" type="Indian/Mauritius"/>
|
||||
<mapZone other="Mauritius Standard Time" territory="MU" type="Indian/Mauritius"/>
|
||||
<mapZone other="Mauritius Standard Time" territory="RE" type="Indian/Reunion"/>
|
||||
<mapZone other="Mauritius Standard Time" territory="SC" type="Indian/Mahe"/>
|
||||
|
||||
<!-- (UTC+04:00) Tbilisi -->
|
||||
<mapZone other="Georgian Standard Time" territory="001" type="Asia/Tbilisi"/>
|
||||
<mapZone other="Georgian Standard Time" territory="GE" type="Asia/Tbilisi"/>
|
||||
|
||||
<!-- (UTC+04:00) Yerevan -->
|
||||
<mapZone other="Caucasus Standard Time" territory="001" type="Asia/Yerevan"/>
|
||||
<mapZone other="Caucasus Standard Time" territory="AM" type="Asia/Yerevan"/>
|
||||
|
||||
<!-- (UTC+04:30) Kabul -->
|
||||
<mapZone other="Afghanistan Standard Time" territory="001" type="Asia/Kabul"/>
|
||||
<mapZone other="Afghanistan Standard Time" territory="AF" type="Asia/Kabul"/>
|
||||
|
||||
<!-- (UTC+05:00) Ashgabat, Tashkent -->
|
||||
<mapZone other="West Asia Standard Time" territory="001" type="Asia/Tashkent"/>
|
||||
<mapZone other="West Asia Standard Time" territory="AQ" type="Antarctica/Mawson"/>
|
||||
<mapZone other="West Asia Standard Time" territory="KZ" type="Asia/Oral Asia/Aqtau Asia/Aqtobe"/>
|
||||
<mapZone other="West Asia Standard Time" territory="MV" type="Indian/Maldives"/>
|
||||
<mapZone other="West Asia Standard Time" territory="TF" type="Indian/Kerguelen"/>
|
||||
<mapZone other="West Asia Standard Time" territory="TJ" type="Asia/Dushanbe"/>
|
||||
<mapZone other="West Asia Standard Time" territory="TM" type="Asia/Ashgabat"/>
|
||||
<mapZone other="West Asia Standard Time" territory="UZ" type="Asia/Tashkent Asia/Samarkand"/>
|
||||
<mapZone other="West Asia Standard Time" territory="ZZ" type="Etc/GMT-5"/>
|
||||
|
||||
<!-- (UTC+05:00) Ekaterinburg -->
|
||||
<mapZone other="Ekaterinburg Standard Time" territory="001" type="Asia/Yekaterinburg"/>
|
||||
<mapZone other="Ekaterinburg Standard Time" territory="RU" type="Asia/Yekaterinburg"/>
|
||||
|
||||
<!-- (UTC+05:00) Islamabad, Karachi -->
|
||||
<mapZone other="Pakistan Standard Time" territory="001" type="Asia/Karachi"/>
|
||||
<mapZone other="Pakistan Standard Time" territory="PK" type="Asia/Karachi"/>
|
||||
|
||||
<!-- (UTC+05:30) Chennai, Kolkata, Mumbai, New Delhi -->
|
||||
<mapZone other="India Standard Time" territory="001" type="Asia/Calcutta"/>
|
||||
<mapZone other="India Standard Time" territory="IN" type="Asia/Calcutta"/>
|
||||
|
||||
<!-- (UTC+05:30) Sri Jayawardenepura -->
|
||||
<mapZone other="Sri Lanka Standard Time" territory="001" type="Asia/Colombo"/>
|
||||
<mapZone other="Sri Lanka Standard Time" territory="LK" type="Asia/Colombo"/>
|
||||
|
||||
<!-- (UTC+05:45) Kathmandu -->
|
||||
<mapZone other="Nepal Standard Time" territory="001" type="Asia/Katmandu"/>
|
||||
<mapZone other="Nepal Standard Time" territory="NP" type="Asia/Katmandu"/>
|
||||
|
||||
<!-- (UTC+06:00) Astana -->
|
||||
<mapZone other="Central Asia Standard Time" territory="001" type="Asia/Almaty"/>
|
||||
<mapZone other="Central Asia Standard Time" territory="AQ" type="Antarctica/Vostok"/>
|
||||
<mapZone other="Central Asia Standard Time" territory="CN" type="Asia/Urumqi"/>
|
||||
<mapZone other="Central Asia Standard Time" territory="IO" type="Indian/Chagos"/>
|
||||
<mapZone other="Central Asia Standard Time" territory="KG" type="Asia/Bishkek"/>
|
||||
<mapZone other="Central Asia Standard Time" territory="KZ" type="Asia/Almaty Asia/Qyzylorda"/>
|
||||
<mapZone other="Central Asia Standard Time" territory="ZZ" type="Etc/GMT-6"/>
|
||||
|
||||
<!-- (UTC+06:00) Dhaka -->
|
||||
<mapZone other="Bangladesh Standard Time" territory="001" type="Asia/Dhaka"/>
|
||||
<mapZone other="Bangladesh Standard Time" territory="BD" type="Asia/Dhaka"/>
|
||||
<mapZone other="Bangladesh Standard Time" territory="BT" type="Asia/Thimphu"/>
|
||||
|
||||
<!-- (UTC+06:00) Omsk -->
|
||||
<mapZone other="Omsk Standard Time" territory="001" type="Asia/Omsk"/>
|
||||
<mapZone other="Omsk Standard Time" territory="RU" type="Asia/Omsk"/>
|
||||
|
||||
<!-- (UTC+06:30) Yangon (Rangoon) -->
|
||||
<mapZone other="Myanmar Standard Time" territory="001" type="Asia/Rangoon"/>
|
||||
<mapZone other="Myanmar Standard Time" territory="CC" type="Indian/Cocos"/>
|
||||
<mapZone other="Myanmar Standard Time" territory="MM" type="Asia/Rangoon"/>
|
||||
|
||||
<!-- (UTC+07:00) Bangkok, Hanoi, Jakarta -->
|
||||
<mapZone other="SE Asia Standard Time" territory="001" type="Asia/Bangkok"/>
|
||||
<mapZone other="SE Asia Standard Time" territory="AQ" type="Antarctica/Davis"/>
|
||||
<mapZone other="SE Asia Standard Time" territory="CX" type="Indian/Christmas"/>
|
||||
<mapZone other="SE Asia Standard Time" territory="ID" type="Asia/Jakarta Asia/Pontianak"/>
|
||||
<mapZone other="SE Asia Standard Time" territory="KH" type="Asia/Phnom_Penh"/>
|
||||
<mapZone other="SE Asia Standard Time" territory="LA" type="Asia/Vientiane"/>
|
||||
<mapZone other="SE Asia Standard Time" territory="TH" type="Asia/Bangkok"/>
|
||||
<mapZone other="SE Asia Standard Time" territory="VN" type="Asia/Saigon"/>
|
||||
<mapZone other="SE Asia Standard Time" territory="ZZ" type="Etc/GMT-7"/>
|
||||
|
||||
<!-- (UTC+07:00) Barnaul, Gorno-Altaysk -->
|
||||
<mapZone other="Altai Standard Time" territory="001" type="Asia/Barnaul"/>
|
||||
<mapZone other="Altai Standard Time" territory="RU" type="Asia/Barnaul"/>
|
||||
|
||||
<!-- (UTC+07:00) Hovd -->
|
||||
<mapZone other="W. Mongolia Standard Time" territory="001" type="Asia/Hovd"/>
|
||||
<mapZone other="W. Mongolia Standard Time" territory="MN" type="Asia/Hovd"/>
|
||||
|
||||
<!-- (UTC+07:00) Krasnoyarsk -->
|
||||
<mapZone other="North Asia Standard Time" territory="001" type="Asia/Krasnoyarsk"/>
|
||||
<mapZone other="North Asia Standard Time" territory="RU" type="Asia/Krasnoyarsk Asia/Novokuznetsk"/>
|
||||
|
||||
<!-- (UTC+07:00) Novosibirsk -->
|
||||
<mapZone other="N. Central Asia Standard Time" territory="001" type="Asia/Novosibirsk"/>
|
||||
<mapZone other="N. Central Asia Standard Time" territory="RU" type="Asia/Novosibirsk"/>
|
||||
|
||||
<!-- (UTC+07:00) Tomsk -->
|
||||
<mapZone other="Tomsk Standard Time" territory="001" type="Asia/Tomsk"/>
|
||||
<mapZone other="Tomsk Standard Time" territory="RU" type="Asia/Tomsk"/>
|
||||
|
||||
<!-- (UTC+08:00) Beijing, Chongqing, Hong Kong, Urumqi -->
|
||||
<mapZone other="China Standard Time" territory="001" type="Asia/Shanghai"/>
|
||||
<mapZone other="China Standard Time" territory="CN" type="Asia/Shanghai"/>
|
||||
<mapZone other="China Standard Time" territory="HK" type="Asia/Hong_Kong"/>
|
||||
<mapZone other="China Standard Time" territory="MO" type="Asia/Macau"/>
|
||||
|
||||
<!-- (UTC+08:00) Irkutsk -->
|
||||
<mapZone other="North Asia East Standard Time" territory="001" type="Asia/Irkutsk"/>
|
||||
<mapZone other="North Asia East Standard Time" territory="RU" type="Asia/Irkutsk"/>
|
||||
|
||||
<!-- (UTC+08:00) Kuala Lumpur, Singapore -->
|
||||
<mapZone other="Singapore Standard Time" territory="001" type="Asia/Singapore"/>
|
||||
<mapZone other="Singapore Standard Time" territory="BN" type="Asia/Brunei"/>
|
||||
<mapZone other="Singapore Standard Time" territory="ID" type="Asia/Makassar"/>
|
||||
<mapZone other="Singapore Standard Time" territory="MY" type="Asia/Kuala_Lumpur Asia/Kuching"/>
|
||||
<mapZone other="Singapore Standard Time" territory="PH" type="Asia/Manila"/>
|
||||
<mapZone other="Singapore Standard Time" territory="SG" type="Asia/Singapore"/>
|
||||
<mapZone other="Singapore Standard Time" territory="ZZ" type="Etc/GMT-8"/>
|
||||
|
||||
<!-- (UTC+08:00) Perth -->
|
||||
<mapZone other="W. Australia Standard Time" territory="001" type="Australia/Perth"/>
|
||||
<mapZone other="W. Australia Standard Time" territory="AU" type="Australia/Perth"/>
|
||||
|
||||
<!-- (UTC+08:00) Taipei -->
|
||||
<mapZone other="Taipei Standard Time" territory="001" type="Asia/Taipei"/>
|
||||
<mapZone other="Taipei Standard Time" territory="TW" type="Asia/Taipei"/>
|
||||
|
||||
<!-- (UTC+08:00) Ulaanbaatar -->
|
||||
<mapZone other="Ulaanbaatar Standard Time" territory="001" type="Asia/Ulaanbaatar"/>
|
||||
<mapZone other="Ulaanbaatar Standard Time" territory="MN" type="Asia/Ulaanbaatar Asia/Choibalsan"/>
|
||||
|
||||
<!-- (UTC+08:30) Pyongyang -->
|
||||
<mapZone other="North Korea Standard Time" territory="001" type="Asia/Pyongyang"/>
|
||||
<mapZone other="North Korea Standard Time" territory="KP" type="Asia/Pyongyang"/>
|
||||
|
||||
<!-- (UTC+08:45) Eucla -->
|
||||
<mapZone other="Aus Central W. Standard Time" territory="001" type="Australia/Eucla"/>
|
||||
<mapZone other="Aus Central W. Standard Time" territory="AU" type="Australia/Eucla"/>
|
||||
|
||||
<!-- (UTC+09:00) Chita -->
|
||||
<mapZone other="Transbaikal Standard Time" territory="001" type="Asia/Chita"/>
|
||||
<mapZone other="Transbaikal Standard Time" territory="RU" type="Asia/Chita"/>
|
||||
|
||||
<!-- (UTC+09:00) Osaka, Sapporo, Tokyo -->
|
||||
<mapZone other="Tokyo Standard Time" territory="001" type="Asia/Tokyo"/>
|
||||
<mapZone other="Tokyo Standard Time" territory="ID" type="Asia/Jayapura"/>
|
||||
<mapZone other="Tokyo Standard Time" territory="JP" type="Asia/Tokyo"/>
|
||||
<mapZone other="Tokyo Standard Time" territory="PW" type="Pacific/Palau"/>
|
||||
<mapZone other="Tokyo Standard Time" territory="TL" type="Asia/Dili"/>
|
||||
<mapZone other="Tokyo Standard Time" territory="ZZ" type="Etc/GMT-9"/>
|
||||
|
||||
<!-- (UTC+09:00) Seoul -->
|
||||
<mapZone other="Korea Standard Time" territory="001" type="Asia/Seoul"/>
|
||||
<mapZone other="Korea Standard Time" territory="KR" type="Asia/Seoul"/>
|
||||
|
||||
<!-- (UTC+09:00) Yakutsk -->
|
||||
<mapZone other="Yakutsk Standard Time" territory="001" type="Asia/Yakutsk"/>
|
||||
<mapZone other="Yakutsk Standard Time" territory="RU" type="Asia/Yakutsk Asia/Khandyga"/>
|
||||
|
||||
<!-- (UTC+09:30) Adelaide -->
|
||||
<mapZone other="Cen. Australia Standard Time" territory="001" type="Australia/Adelaide"/>
|
||||
<mapZone other="Cen. Australia Standard Time" territory="AU" type="Australia/Adelaide Australia/Broken_Hill"/>
|
||||
|
||||
<!-- (UTC+09:30) Darwin -->
|
||||
<mapZone other="AUS Central Standard Time" territory="001" type="Australia/Darwin"/>
|
||||
<mapZone other="AUS Central Standard Time" territory="AU" type="Australia/Darwin"/>
|
||||
|
||||
<!-- (UTC+10:00) Brisbane -->
|
||||
<mapZone other="E. Australia Standard Time" territory="001" type="Australia/Brisbane"/>
|
||||
<mapZone other="E. Australia Standard Time" territory="AU" type="Australia/Brisbane Australia/Lindeman"/>
|
||||
|
||||
<!-- (UTC+10:00) Canberra, Melbourne, Sydney -->
|
||||
<mapZone other="AUS Eastern Standard Time" territory="001" type="Australia/Sydney"/>
|
||||
<mapZone other="AUS Eastern Standard Time" territory="AU" type="Australia/Sydney Australia/Melbourne"/>
|
||||
|
||||
<!-- (UTC+10:00) Guam, Port Moresby -->
|
||||
<mapZone other="West Pacific Standard Time" territory="001" type="Pacific/Port_Moresby"/>
|
||||
<mapZone other="West Pacific Standard Time" territory="AQ" type="Antarctica/DumontDUrville"/>
|
||||
<mapZone other="West Pacific Standard Time" territory="FM" type="Pacific/Truk"/>
|
||||
<mapZone other="West Pacific Standard Time" territory="GU" type="Pacific/Guam"/>
|
||||
<mapZone other="West Pacific Standard Time" territory="MP" type="Pacific/Saipan"/>
|
||||
<mapZone other="West Pacific Standard Time" territory="PG" type="Pacific/Port_Moresby"/>
|
||||
<mapZone other="West Pacific Standard Time" territory="ZZ" type="Etc/GMT-10"/>
|
||||
|
||||
<!-- (UTC+10:00) Hobart -->
|
||||
<mapZone other="Tasmania Standard Time" territory="001" type="Australia/Hobart"/>
|
||||
<mapZone other="Tasmania Standard Time" territory="AU" type="Australia/Hobart Australia/Currie"/>
|
||||
|
||||
<!-- (UTC+10:00) Vladivostok -->
|
||||
<mapZone other="Vladivostok Standard Time" territory="001" type="Asia/Vladivostok"/>
|
||||
<mapZone other="Vladivostok Standard Time" territory="RU" type="Asia/Vladivostok Asia/Ust-Nera"/>
|
||||
|
||||
<!-- (UTC+10:30) Lord Howe Island -->
|
||||
<mapZone other="Lord Howe Standard Time" territory="001" type="Australia/Lord_Howe"/>
|
||||
<mapZone other="Lord Howe Standard Time" territory="AU" type="Australia/Lord_Howe"/>
|
||||
|
||||
<!-- (UTC+11:00) Bougainville Island -->
|
||||
<mapZone other="Bougainville Standard Time" territory="001" type="Pacific/Bougainville"/>
|
||||
<mapZone other="Bougainville Standard Time" territory="PG" type="Pacific/Bougainville"/>
|
||||
|
||||
<!-- (UTC+11:00) Chokurdakh -->
|
||||
<mapZone other="Russia Time Zone 10" territory="001" type="Asia/Srednekolymsk"/>
|
||||
<mapZone other="Russia Time Zone 10" territory="RU" type="Asia/Srednekolymsk"/>
|
||||
|
||||
<!-- (UTC+11:00) Magadan -->
|
||||
<mapZone other="Magadan Standard Time" territory="001" type="Asia/Magadan"/>
|
||||
<mapZone other="Magadan Standard Time" territory="RU" type="Asia/Magadan"/>
|
||||
|
||||
<!-- (UTC+11:00) Norfolk Island -->
|
||||
<mapZone other="Norfolk Standard Time" territory="001" type="Pacific/Norfolk"/>
|
||||
<mapZone other="Norfolk Standard Time" territory="NF" type="Pacific/Norfolk"/>
|
||||
|
||||
<!-- (UTC+11:00) Sakhalin -->
|
||||
<mapZone other="Sakhalin Standard Time" territory="001" type="Asia/Sakhalin"/>
|
||||
<mapZone other="Sakhalin Standard Time" territory="RU" type="Asia/Sakhalin"/>
|
||||
|
||||
<!-- (UTC+11:00) Solomon Is., New Caledonia -->
|
||||
<mapZone other="Central Pacific Standard Time" territory="001" type="Pacific/Guadalcanal"/>
|
||||
<mapZone other="Central Pacific Standard Time" territory="AQ" type="Antarctica/Casey"/>
|
||||
<mapZone other="Central Pacific Standard Time" territory="AU" type="Antarctica/Macquarie"/>
|
||||
<mapZone other="Central Pacific Standard Time" territory="FM" type="Pacific/Ponape Pacific/Kosrae"/>
|
||||
<mapZone other="Central Pacific Standard Time" territory="NC" type="Pacific/Noumea"/>
|
||||
<mapZone other="Central Pacific Standard Time" territory="SB" type="Pacific/Guadalcanal"/>
|
||||
<mapZone other="Central Pacific Standard Time" territory="VU" type="Pacific/Efate"/>
|
||||
<mapZone other="Central Pacific Standard Time" territory="ZZ" type="Etc/GMT-11"/>
|
||||
|
||||
<!-- (UTC+12:00) Anadyr, Petropavlovsk-Kamchatsky -->
|
||||
<mapZone other="Russia Time Zone 11" territory="001" type="Asia/Kamchatka"/>
|
||||
<mapZone other="Russia Time Zone 11" territory="RU" type="Asia/Kamchatka Asia/Anadyr"/>
|
||||
|
||||
<!-- (UTC+12:00) Auckland, Wellington -->
|
||||
<mapZone other="New Zealand Standard Time" territory="001" type="Pacific/Auckland"/>
|
||||
<mapZone other="New Zealand Standard Time" territory="AQ" type="Antarctica/McMurdo"/>
|
||||
<mapZone other="New Zealand Standard Time" territory="NZ" type="Pacific/Auckland"/>
|
||||
|
||||
<!-- (UTC+12:00) Coordinated Universal Time+12 -->
|
||||
<mapZone other="UTC+12" territory="001" type="Etc/GMT-12"/>
|
||||
<mapZone other="UTC+12" territory="KI" type="Pacific/Tarawa"/>
|
||||
<mapZone other="UTC+12" territory="MH" type="Pacific/Majuro Pacific/Kwajalein"/>
|
||||
<mapZone other="UTC+12" territory="NR" type="Pacific/Nauru"/>
|
||||
<mapZone other="UTC+12" territory="TV" type="Pacific/Funafuti"/>
|
||||
<mapZone other="UTC+12" territory="UM" type="Pacific/Wake"/>
|
||||
<mapZone other="UTC+12" territory="WF" type="Pacific/Wallis"/>
|
||||
<mapZone other="UTC+12" territory="ZZ" type="Etc/GMT-12"/>
|
||||
|
||||
<!-- (UTC+12:00) Fiji -->
|
||||
<mapZone other="Fiji Standard Time" territory="001" type="Pacific/Fiji"/>
|
||||
<mapZone other="Fiji Standard Time" territory="FJ" type="Pacific/Fiji"/>
|
||||
|
||||
<!-- (UTC+12:45) Chatham Islands -->
|
||||
<mapZone other="Chatham Islands Standard Time" territory="001" type="Pacific/Chatham"/>
|
||||
<mapZone other="Chatham Islands Standard Time" territory="NZ" type="Pacific/Chatham"/>
|
||||
|
||||
<!-- (UTC+13:00) Nuku'alofa -->
|
||||
<mapZone other="Tonga Standard Time" territory="001" type="Pacific/Tongatapu"/>
|
||||
<mapZone other="Tonga Standard Time" territory="KI" type="Pacific/Enderbury"/>
|
||||
<mapZone other="Tonga Standard Time" territory="TK" type="Pacific/Fakaofo"/>
|
||||
<mapZone other="Tonga Standard Time" territory="TO" type="Pacific/Tongatapu"/>
|
||||
<mapZone other="Tonga Standard Time" territory="ZZ" type="Etc/GMT-13"/>
|
||||
|
||||
<!-- (UTC+13:00) Samoa -->
|
||||
<mapZone other="Samoa Standard Time" territory="001" type="Pacific/Apia"/>
|
||||
<mapZone other="Samoa Standard Time" territory="WS" type="Pacific/Apia"/>
|
||||
|
||||
<!-- (UTC+14:00) Kiritimati Island -->
|
||||
<mapZone other="Line Islands Standard Time" territory="001" type="Pacific/Kiritimati"/>
|
||||
<mapZone other="Line Islands Standard Time" territory="KI" type="Pacific/Kiritimati"/>
|
||||
<mapZone other="Line Islands Standard Time" territory="ZZ" type="Etc/GMT-14"/>
|
||||
</mapTimezones>
|
||||
</windowsZones>
|
||||
</supplementalData>
|
46
resources/timezones/generate-timezone-map.php
Executable file
46
resources/timezones/generate-timezone-map.php
Executable file
|
@ -0,0 +1,46 @@
|
|||
#!/usr/bin/env php
|
||||
<?php
|
||||
|
||||
$root = dirname(dirname(dirname(__FILE__)));
|
||||
require_once $root.'/scripts/init/init-script.php';
|
||||
|
||||
$xml = $root.'/externals/cldr/cldr_windows_timezones.xml';
|
||||
$xml = Filesystem::readFile($xml);
|
||||
$xml = new SimpleXMLElement($xml);
|
||||
|
||||
$result_map = array();
|
||||
|
||||
$ignore = array(
|
||||
'UTC',
|
||||
'UTC-11',
|
||||
'UTC-02',
|
||||
'UTC-08',
|
||||
'UTC-09',
|
||||
'UTC+12',
|
||||
);
|
||||
$ignore = array_fuse($ignore);
|
||||
|
||||
$zones = $xml->windowsZones->mapTimezones->mapZone;
|
||||
foreach ($zones as $zone) {
|
||||
$windows_name = (string)$zone['other'];
|
||||
$target_name = (string)$zone['type'];
|
||||
|
||||
// Ignore the offset-based timezones from the CLDR map, since we handle
|
||||
// these later.
|
||||
if (isset($ignore[$windows_name])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// We've already seen this timezone so we don't need to add it to the map
|
||||
// again.
|
||||
if (isset($result_map[$windows_name])) {
|
||||
continue;
|
||||
}
|
||||
|
||||
$result_map[$windows_name] = $target_name;
|
||||
}
|
||||
|
||||
asort($result_map);
|
||||
|
||||
echo id(new PhutilJSON())
|
||||
->encodeFormatted($result_map);
|
126
resources/timezones/windows-timezones.json
Normal file
126
resources/timezones/windows-timezones.json
Normal file
|
@ -0,0 +1,126 @@
|
|||
{
|
||||
"Egypt Standard Time": "Africa/Cairo",
|
||||
"Morocco Standard Time": "Africa/Casablanca",
|
||||
"South Africa Standard Time": "Africa/Johannesburg",
|
||||
"W. Central Africa Standard Time": "Africa/Lagos",
|
||||
"E. Africa Standard Time": "Africa/Nairobi",
|
||||
"Libya Standard Time": "Africa/Tripoli",
|
||||
"Namibia Standard Time": "Africa/Windhoek",
|
||||
"Aleutian Standard Time": "America/Adak",
|
||||
"Alaskan Standard Time": "America/Anchorage",
|
||||
"Tocantins Standard Time": "America/Araguaina",
|
||||
"Paraguay Standard Time": "America/Asuncion",
|
||||
"Bahia Standard Time": "America/Bahia",
|
||||
"SA Pacific Standard Time": "America/Bogota",
|
||||
"Argentina Standard Time": "America/Buenos_Aires",
|
||||
"Eastern Standard Time (Mexico)": "America/Cancun",
|
||||
"Venezuela Standard Time": "America/Caracas",
|
||||
"SA Eastern Standard Time": "America/Cayenne",
|
||||
"Central Standard Time": "America/Chicago",
|
||||
"Mountain Standard Time (Mexico)": "America/Chihuahua",
|
||||
"Central Brazilian Standard Time": "America/Cuiaba",
|
||||
"Mountain Standard Time": "America/Denver",
|
||||
"Greenland Standard Time": "America/Godthab",
|
||||
"Turks And Caicos Standard Time": "America/Grand_Turk",
|
||||
"Central America Standard Time": "America/Guatemala",
|
||||
"Atlantic Standard Time": "America/Halifax",
|
||||
"Cuba Standard Time": "America/Havana",
|
||||
"US Eastern Standard Time": "America/Indianapolis",
|
||||
"SA Western Standard Time": "America/La_Paz",
|
||||
"Pacific Standard Time": "America/Los_Angeles",
|
||||
"Central Standard Time (Mexico)": "America/Mexico_City",
|
||||
"Saint Pierre Standard Time": "America/Miquelon",
|
||||
"Montevideo Standard Time": "America/Montevideo",
|
||||
"Eastern Standard Time": "America/New_York",
|
||||
"US Mountain Standard Time": "America/Phoenix",
|
||||
"Haiti Standard Time": "America/Port-au-Prince",
|
||||
"Canada Central Standard Time": "America/Regina",
|
||||
"Pacific SA Standard Time": "America/Santiago",
|
||||
"E. South America Standard Time": "America/Sao_Paulo",
|
||||
"Newfoundland Standard Time": "America/St_Johns",
|
||||
"Pacific Standard Time (Mexico)": "America/Tijuana",
|
||||
"Central Asia Standard Time": "Asia/Almaty",
|
||||
"Jordan Standard Time": "Asia/Amman",
|
||||
"Arabic Standard Time": "Asia/Baghdad",
|
||||
"Azerbaijan Standard Time": "Asia/Baku",
|
||||
"SE Asia Standard Time": "Asia/Bangkok",
|
||||
"Altai Standard Time": "Asia/Barnaul",
|
||||
"Middle East Standard Time": "Asia/Beirut",
|
||||
"India Standard Time": "Asia/Calcutta",
|
||||
"Transbaikal Standard Time": "Asia/Chita",
|
||||
"Sri Lanka Standard Time": "Asia/Colombo",
|
||||
"Syria Standard Time": "Asia/Damascus",
|
||||
"Bangladesh Standard Time": "Asia/Dhaka",
|
||||
"Arabian Standard Time": "Asia/Dubai",
|
||||
"West Bank Standard Time": "Asia/Hebron",
|
||||
"W. Mongolia Standard Time": "Asia/Hovd",
|
||||
"North Asia East Standard Time": "Asia/Irkutsk",
|
||||
"Israel Standard Time": "Asia/Jerusalem",
|
||||
"Afghanistan Standard Time": "Asia/Kabul",
|
||||
"Russia Time Zone 11": "Asia/Kamchatka",
|
||||
"Pakistan Standard Time": "Asia/Karachi",
|
||||
"Nepal Standard Time": "Asia/Katmandu",
|
||||
"North Asia Standard Time": "Asia/Krasnoyarsk",
|
||||
"Magadan Standard Time": "Asia/Magadan",
|
||||
"N. Central Asia Standard Time": "Asia/Novosibirsk",
|
||||
"Omsk Standard Time": "Asia/Omsk",
|
||||
"North Korea Standard Time": "Asia/Pyongyang",
|
||||
"Myanmar Standard Time": "Asia/Rangoon",
|
||||
"Arab Standard Time": "Asia/Riyadh",
|
||||
"Sakhalin Standard Time": "Asia/Sakhalin",
|
||||
"Korea Standard Time": "Asia/Seoul",
|
||||
"China Standard Time": "Asia/Shanghai",
|
||||
"Singapore Standard Time": "Asia/Singapore",
|
||||
"Russia Time Zone 10": "Asia/Srednekolymsk",
|
||||
"Taipei Standard Time": "Asia/Taipei",
|
||||
"West Asia Standard Time": "Asia/Tashkent",
|
||||
"Georgian Standard Time": "Asia/Tbilisi",
|
||||
"Iran Standard Time": "Asia/Tehran",
|
||||
"Tokyo Standard Time": "Asia/Tokyo",
|
||||
"Tomsk Standard Time": "Asia/Tomsk",
|
||||
"Ulaanbaatar Standard Time": "Asia/Ulaanbaatar",
|
||||
"Vladivostok Standard Time": "Asia/Vladivostok",
|
||||
"Yakutsk Standard Time": "Asia/Yakutsk",
|
||||
"Ekaterinburg Standard Time": "Asia/Yekaterinburg",
|
||||
"Caucasus Standard Time": "Asia/Yerevan",
|
||||
"Azores Standard Time": "Atlantic/Azores",
|
||||
"Cape Verde Standard Time": "Atlantic/Cape_Verde",
|
||||
"Greenwich Standard Time": "Atlantic/Reykjavik",
|
||||
"Cen. Australia Standard Time": "Australia/Adelaide",
|
||||
"E. Australia Standard Time": "Australia/Brisbane",
|
||||
"AUS Central Standard Time": "Australia/Darwin",
|
||||
"Aus Central W. Standard Time": "Australia/Eucla",
|
||||
"Tasmania Standard Time": "Australia/Hobart",
|
||||
"Lord Howe Standard Time": "Australia/Lord_Howe",
|
||||
"W. Australia Standard Time": "Australia/Perth",
|
||||
"AUS Eastern Standard Time": "Australia/Sydney",
|
||||
"Dateline Standard Time": "Etc/GMT+12",
|
||||
"Astrakhan Standard Time": "Europe/Astrakhan",
|
||||
"W. Europe Standard Time": "Europe/Berlin",
|
||||
"GTB Standard Time": "Europe/Bucharest",
|
||||
"Central Europe Standard Time": "Europe/Budapest",
|
||||
"E. Europe Standard Time": "Europe/Chisinau",
|
||||
"Turkey Standard Time": "Europe/Istanbul",
|
||||
"Kaliningrad Standard Time": "Europe/Kaliningrad",
|
||||
"FLE Standard Time": "Europe/Kiev",
|
||||
"GMT Standard Time": "Europe/London",
|
||||
"Belarus Standard Time": "Europe/Minsk",
|
||||
"Russian Standard Time": "Europe/Moscow",
|
||||
"Romance Standard Time": "Europe/Paris",
|
||||
"Russia Time Zone 3": "Europe/Samara",
|
||||
"Central European Standard Time": "Europe/Warsaw",
|
||||
"Mauritius Standard Time": "Indian/Mauritius",
|
||||
"Samoa Standard Time": "Pacific/Apia",
|
||||
"New Zealand Standard Time": "Pacific/Auckland",
|
||||
"Bougainville Standard Time": "Pacific/Bougainville",
|
||||
"Chatham Islands Standard Time": "Pacific/Chatham",
|
||||
"Easter Island Standard Time": "Pacific/Easter",
|
||||
"Fiji Standard Time": "Pacific/Fiji",
|
||||
"Central Pacific Standard Time": "Pacific/Guadalcanal",
|
||||
"Hawaiian Standard Time": "Pacific/Honolulu",
|
||||
"Line Islands Standard Time": "Pacific/Kiritimati",
|
||||
"Marquesas Standard Time": "Pacific/Marquesas",
|
||||
"Norfolk Standard Time": "Pacific/Norfolk",
|
||||
"West Pacific Standard Time": "Pacific/Port_Moresby",
|
||||
"Tonga Standard Time": "Pacific/Tongatapu"
|
||||
}
|
131
scripts/daemon/exec/exec_daemon.php
Executable file
131
scripts/daemon/exec/exec_daemon.php
Executable file
|
@ -0,0 +1,131 @@
|
|||
#!/usr/bin/env php
|
||||
<?php
|
||||
|
||||
if (function_exists('pcntl_async_signals')) {
|
||||
pcntl_async_signals(true);
|
||||
} else {
|
||||
declare(ticks = 1);
|
||||
}
|
||||
|
||||
require_once dirname(__FILE__).'/../../__init_script__.php';
|
||||
|
||||
if (!posix_isatty(STDOUT)) {
|
||||
$sid = posix_setsid();
|
||||
if ($sid <= 0) {
|
||||
throw new Exception(pht('Failed to create new process session!'));
|
||||
}
|
||||
}
|
||||
|
||||
$args = new PhutilArgumentParser($argv);
|
||||
$args->setTagline(pht('daemon executor'));
|
||||
$args->setSynopsis(<<<EOHELP
|
||||
**exec_daemon.php** [__options__] __daemon__ ...
|
||||
Run an instance of __daemon__.
|
||||
EOHELP
|
||||
);
|
||||
$args->parse(
|
||||
array(
|
||||
array(
|
||||
'name' => 'trace',
|
||||
'help' => pht('Enable debug tracing.'),
|
||||
),
|
||||
array(
|
||||
'name' => 'trace-memory',
|
||||
'help' => pht('Enable debug memory tracing.'),
|
||||
),
|
||||
array(
|
||||
'name' => 'verbose',
|
||||
'help' => pht('Enable verbose activity logging.'),
|
||||
),
|
||||
array(
|
||||
'name' => 'label',
|
||||
'short' => 'l',
|
||||
'param' => 'label',
|
||||
'help' => pht(
|
||||
'Optional process label. Makes "%s" nicer, no behavioral effects.',
|
||||
'ps'),
|
||||
),
|
||||
array(
|
||||
'name' => 'daemon',
|
||||
'wildcard' => true,
|
||||
),
|
||||
));
|
||||
|
||||
$trace_memory = $args->getArg('trace-memory');
|
||||
$trace_mode = $args->getArg('trace') || $trace_memory;
|
||||
$verbose = $args->getArg('verbose');
|
||||
|
||||
if (function_exists('posix_isatty') && posix_isatty(STDIN)) {
|
||||
fprintf(STDERR, pht('Reading daemon configuration from stdin...')."\n");
|
||||
}
|
||||
$config = @file_get_contents('php://stdin');
|
||||
$config = id(new PhutilJSONParser())->parse($config);
|
||||
|
||||
PhutilTypeSpec::checkMap(
|
||||
$config,
|
||||
array(
|
||||
'log' => 'optional string|null',
|
||||
'argv' => 'optional list<wild>',
|
||||
'load' => 'optional list<string>',
|
||||
'down' => 'optional int',
|
||||
));
|
||||
|
||||
$log = idx($config, 'log');
|
||||
|
||||
if ($log) {
|
||||
ini_set('error_log', $log);
|
||||
PhutilErrorHandler::setErrorListener(array('PhutilDaemon', 'errorListener'));
|
||||
}
|
||||
|
||||
$load = idx($config, 'load', array());
|
||||
foreach ($load as $library) {
|
||||
$library = Filesystem::resolvePath($library);
|
||||
phutil_load_library($library);
|
||||
}
|
||||
|
||||
PhutilErrorHandler::initialize();
|
||||
|
||||
$daemon = $args->getArg('daemon');
|
||||
if (!$daemon) {
|
||||
throw new PhutilArgumentUsageException(
|
||||
pht('Specify which class of daemon to start.'));
|
||||
} else if (count($daemon) > 1) {
|
||||
throw new PhutilArgumentUsageException(
|
||||
pht('Specify exactly one daemon to start.'));
|
||||
} else {
|
||||
$daemon = head($daemon);
|
||||
if (!class_exists($daemon)) {
|
||||
throw new PhutilArgumentUsageException(
|
||||
pht(
|
||||
'No class "%s" exists in any known library.',
|
||||
$daemon));
|
||||
} else if (!is_subclass_of($daemon, 'PhutilDaemon')) {
|
||||
throw new PhutilArgumentUsageException(
|
||||
pht(
|
||||
'Class "%s" is not a subclass of "%s".',
|
||||
$daemon,
|
||||
'PhutilDaemon'));
|
||||
}
|
||||
}
|
||||
|
||||
$argv = idx($config, 'argv', array());
|
||||
$daemon = newv($daemon, array($argv));
|
||||
|
||||
if ($trace_mode) {
|
||||
$daemon->setTraceMode();
|
||||
}
|
||||
|
||||
if ($trace_memory) {
|
||||
$daemon->setTraceMemory();
|
||||
}
|
||||
|
||||
if ($verbose) {
|
||||
$daemon->setVerbose(true);
|
||||
}
|
||||
|
||||
$down_duration = idx($config, 'down');
|
||||
if ($down_duration) {
|
||||
$daemon->setScaledownDuration($down_duration);
|
||||
}
|
||||
|
||||
$daemon->execute();
|
|
@ -8,10 +8,14 @@ function init_phabricator_script(array $options) {
|
|||
ini_set(
|
||||
'include_path',
|
||||
$include_path.PATH_SEPARATOR.dirname(__FILE__).'/../../../');
|
||||
@include_once 'libphutil/scripts/__init_script__.php';
|
||||
if (!@constant('__LIBPHUTIL__')) {
|
||||
echo "ERROR: Unable to load libphutil. Update your PHP 'include_path' to ".
|
||||
"include the parent directory of libphutil/.\n";
|
||||
|
||||
$ok = @include_once 'arcanist/scripts/init/init-script.php';
|
||||
if (!$ok) {
|
||||
echo
|
||||
'FATAL ERROR: Unable to load the "Arcanist" library. '.
|
||||
'Put "arcanist/" next to "phabricator/" on disk.';
|
||||
echo "\n";
|
||||
|
||||
exit(1);
|
||||
}
|
||||
|
||||
|
|
|
@ -5623,6 +5623,11 @@ phutil_register_library_map(array(
|
|||
'PhutilCodeSnippetContextFreeGrammar' => 'infrastructure/lipsum/code/PhutilCodeSnippetContextFreeGrammar.php',
|
||||
'PhutilConsoleSyntaxHighlighter' => 'infrastructure/markup/syntax/highlighter/PhutilConsoleSyntaxHighlighter.php',
|
||||
'PhutilContextFreeGrammar' => 'infrastructure/lipsum/PhutilContextFreeGrammar.php',
|
||||
'PhutilDaemon' => 'infrastructure/daemon/PhutilDaemon.php',
|
||||
'PhutilDaemonHandle' => 'infrastructure/daemon/PhutilDaemonHandle.php',
|
||||
'PhutilDaemonOverseer' => 'infrastructure/daemon/PhutilDaemonOverseer.php',
|
||||
'PhutilDaemonOverseerModule' => 'infrastructure/daemon/PhutilDaemonOverseerModule.php',
|
||||
'PhutilDaemonPool' => 'infrastructure/daemon/PhutilDaemonPool.php',
|
||||
'PhutilDefaultSyntaxHighlighter' => 'infrastructure/markup/syntax/highlighter/PhutilDefaultSyntaxHighlighter.php',
|
||||
'PhutilDefaultSyntaxHighlighterEngine' => 'infrastructure/markup/syntax/engine/PhutilDefaultSyntaxHighlighterEngine.php',
|
||||
'PhutilDefaultSyntaxHighlighterEnginePygmentsFuture' => 'infrastructure/markup/syntax/highlighter/pygments/PhutilDefaultSyntaxHighlighterEnginePygmentsFuture.php',
|
||||
|
@ -12522,6 +12527,11 @@ phutil_register_library_map(array(
|
|||
'PhutilCodeSnippetContextFreeGrammar' => 'PhutilContextFreeGrammar',
|
||||
'PhutilConsoleSyntaxHighlighter' => 'Phobject',
|
||||
'PhutilContextFreeGrammar' => 'Phobject',
|
||||
'PhutilDaemon' => 'Phobject',
|
||||
'PhutilDaemonHandle' => 'Phobject',
|
||||
'PhutilDaemonOverseer' => 'Phobject',
|
||||
'PhutilDaemonOverseerModule' => 'Phobject',
|
||||
'PhutilDaemonPool' => 'Phobject',
|
||||
'PhutilDefaultSyntaxHighlighter' => 'Phobject',
|
||||
'PhutilDefaultSyntaxHighlighterEngine' => 'PhutilSyntaxHighlighterEngine',
|
||||
'PhutilDefaultSyntaxHighlighterEnginePygmentsFuture' => 'FutureProxy',
|
||||
|
|
|
@ -849,8 +849,8 @@ final class PhutilICSParser extends Phobject {
|
|||
);
|
||||
|
||||
// Load the map of Windows timezones.
|
||||
$root_path = dirname(phutil_get_library_root('phutil'));
|
||||
$windows_path = $root_path.'/resources/timezones/windows_timezones.json';
|
||||
$root_path = dirname(phutil_get_library_root('phabricator'));
|
||||
$windows_path = $root_path.'/resources/timezones/windows-timezones.json';
|
||||
$windows_data = Filesystem::readFile($windows_path);
|
||||
$windows_zones = phutil_json_decode($windows_data);
|
||||
|
||||
|
|
393
src/infrastructure/daemon/PhutilDaemon.php
Normal file
393
src/infrastructure/daemon/PhutilDaemon.php
Normal file
|
@ -0,0 +1,393 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Scaffolding for implementing robust background processing scripts.
|
||||
*
|
||||
*
|
||||
* Autoscaling
|
||||
* ===========
|
||||
*
|
||||
* Autoscaling automatically launches copies of a daemon when it is busy
|
||||
* (scaling the pool up) and stops them when they're idle (scaling the pool
|
||||
* down). This is appropriate for daemons which perform highly parallelizable
|
||||
* work.
|
||||
*
|
||||
* To make a daemon support autoscaling, the implementation should look
|
||||
* something like this:
|
||||
*
|
||||
* while (!$this->shouldExit()) {
|
||||
* if (work_available()) {
|
||||
* $this->willBeginWork();
|
||||
* do_work();
|
||||
* $this->sleep(0);
|
||||
* } else {
|
||||
* $this->willBeginIdle();
|
||||
* $this->sleep(1);
|
||||
* }
|
||||
* }
|
||||
*
|
||||
* In particular, call @{method:willBeginWork} before becoming busy, and
|
||||
* @{method:willBeginIdle} when no work is available. If the daemon is launched
|
||||
* into an autoscale pool, this will cause the pool to automatically scale up
|
||||
* when busy and down when idle.
|
||||
*
|
||||
* See @{class:PhutilHighIntensityIntervalDaemon} for an example of a simple
|
||||
* autoscaling daemon.
|
||||
*
|
||||
* Launching a daemon which does not make these callbacks into an autoscale
|
||||
* pool will have no effect.
|
||||
*
|
||||
* @task overseer Communicating With the Overseer
|
||||
* @task autoscale Autoscaling Daemon Pools
|
||||
*/
|
||||
abstract class PhutilDaemon extends Phobject {
|
||||
|
||||
const MESSAGETYPE_STDOUT = 'stdout';
|
||||
const MESSAGETYPE_HEARTBEAT = 'heartbeat';
|
||||
const MESSAGETYPE_BUSY = 'busy';
|
||||
const MESSAGETYPE_IDLE = 'idle';
|
||||
const MESSAGETYPE_DOWN = 'down';
|
||||
const MESSAGETYPE_HIBERNATE = 'hibernate';
|
||||
|
||||
const WORKSTATE_BUSY = 'busy';
|
||||
const WORKSTATE_IDLE = 'idle';
|
||||
|
||||
private $argv;
|
||||
private $traceMode;
|
||||
private $traceMemory;
|
||||
private $verbose;
|
||||
private $notifyReceived;
|
||||
private $inGracefulShutdown;
|
||||
private $workState = null;
|
||||
private $idleSince = null;
|
||||
private $scaledownDuration;
|
||||
|
||||
final public function setVerbose($verbose) {
|
||||
$this->verbose = $verbose;
|
||||
return $this;
|
||||
}
|
||||
|
||||
final public function getVerbose() {
|
||||
return $this->verbose;
|
||||
}
|
||||
|
||||
final public function setScaledownDuration($scaledown_duration) {
|
||||
$this->scaledownDuration = $scaledown_duration;
|
||||
return $this;
|
||||
}
|
||||
|
||||
final public function getScaledownDuration() {
|
||||
return $this->scaledownDuration;
|
||||
}
|
||||
|
||||
final public function __construct(array $argv) {
|
||||
$this->argv = $argv;
|
||||
|
||||
$router = PhutilSignalRouter::getRouter();
|
||||
$handler_key = 'daemon.term';
|
||||
if (!$router->getHandler($handler_key)) {
|
||||
$handler = new PhutilCallbackSignalHandler(
|
||||
SIGTERM,
|
||||
__CLASS__.'::onTermSignal');
|
||||
$router->installHandler($handler_key, $handler);
|
||||
}
|
||||
|
||||
pcntl_signal(SIGINT, array($this, 'onGracefulSignal'));
|
||||
pcntl_signal(SIGUSR2, array($this, 'onNotifySignal'));
|
||||
|
||||
// Without discard mode, this consumes unbounded amounts of memory. Keep
|
||||
// memory bounded.
|
||||
PhutilServiceProfiler::getInstance()->enableDiscardMode();
|
||||
|
||||
$this->beginStdoutCapture();
|
||||
}
|
||||
|
||||
final public function __destruct() {
|
||||
$this->endStdoutCapture();
|
||||
}
|
||||
|
||||
final public function stillWorking() {
|
||||
$this->emitOverseerMessage(self::MESSAGETYPE_HEARTBEAT, null);
|
||||
|
||||
if ($this->traceMemory) {
|
||||
$daemon = get_class($this);
|
||||
fprintf(
|
||||
STDERR,
|
||||
"%s %s %s\n",
|
||||
'<RAMS>',
|
||||
$daemon,
|
||||
pht(
|
||||
'Memory Usage: %s KB',
|
||||
new PhutilNumber(memory_get_usage() / 1024, 1)));
|
||||
}
|
||||
}
|
||||
|
||||
final public function shouldExit() {
|
||||
return $this->inGracefulShutdown;
|
||||
}
|
||||
|
||||
final protected function shouldHibernate($duration) {
|
||||
// Don't hibernate if we don't have very long to sleep.
|
||||
if ($duration < 30) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Never hibernate if we're part of a pool and could scale down instead.
|
||||
// We only hibernate the last process to drop the pool size to zero.
|
||||
if ($this->getScaledownDuration()) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// Don't hibernate for too long.
|
||||
$duration = min($duration, phutil_units('3 minutes in seconds'));
|
||||
|
||||
$this->emitOverseerMessage(
|
||||
self::MESSAGETYPE_HIBERNATE,
|
||||
array(
|
||||
'duration' => $duration,
|
||||
));
|
||||
|
||||
$this->log(
|
||||
pht(
|
||||
'Preparing to hibernate for %s second(s).',
|
||||
new PhutilNumber($duration)));
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
final protected function sleep($duration) {
|
||||
$this->notifyReceived = false;
|
||||
$this->willSleep($duration);
|
||||
$this->stillWorking();
|
||||
|
||||
$scale_down = $this->getScaledownDuration();
|
||||
|
||||
$max_sleep = 60;
|
||||
if ($scale_down) {
|
||||
$max_sleep = min($max_sleep, $scale_down);
|
||||
}
|
||||
|
||||
if ($scale_down) {
|
||||
if ($this->workState == self::WORKSTATE_IDLE) {
|
||||
$dur = $this->getIdleDuration();
|
||||
$this->log(pht('Idle for %s seconds.', $dur));
|
||||
}
|
||||
}
|
||||
|
||||
while ($duration > 0 &&
|
||||
!$this->notifyReceived &&
|
||||
!$this->shouldExit()) {
|
||||
|
||||
// If this is an autoscaling clone and we've been idle for too long,
|
||||
// we're going to scale the pool down by exiting and not restarting. The
|
||||
// DOWN message tells the overseer that we don't want to be restarted.
|
||||
if ($scale_down) {
|
||||
if ($this->workState == self::WORKSTATE_IDLE) {
|
||||
if ($this->idleSince && ($this->idleSince + $scale_down < time())) {
|
||||
$this->inGracefulShutdown = true;
|
||||
$this->emitOverseerMessage(self::MESSAGETYPE_DOWN, null);
|
||||
$this->log(
|
||||
pht(
|
||||
'Daemon was idle for more than %s second(s), '.
|
||||
'scaling pool down.',
|
||||
new PhutilNumber($scale_down)));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
sleep(min($duration, $max_sleep));
|
||||
$duration -= $max_sleep;
|
||||
$this->stillWorking();
|
||||
}
|
||||
}
|
||||
|
||||
protected function willSleep($duration) {
|
||||
return;
|
||||
}
|
||||
|
||||
public static function onTermSignal($signo) {
|
||||
self::didCatchSignal($signo);
|
||||
}
|
||||
|
||||
final protected function getArgv() {
|
||||
return $this->argv;
|
||||
}
|
||||
|
||||
final public function execute() {
|
||||
$this->willRun();
|
||||
$this->run();
|
||||
}
|
||||
|
||||
abstract protected function run();
|
||||
|
||||
final public function setTraceMemory() {
|
||||
$this->traceMemory = true;
|
||||
return $this;
|
||||
}
|
||||
|
||||
final public function getTraceMemory() {
|
||||
return $this->traceMemory;
|
||||
}
|
||||
|
||||
final public function setTraceMode() {
|
||||
$this->traceMode = true;
|
||||
PhutilServiceProfiler::installEchoListener();
|
||||
PhutilConsole::getConsole()->getServer()->setEnableLog(true);
|
||||
$this->didSetTraceMode();
|
||||
return $this;
|
||||
}
|
||||
|
||||
final public function getTraceMode() {
|
||||
return $this->traceMode;
|
||||
}
|
||||
|
||||
final public function onGracefulSignal($signo) {
|
||||
self::didCatchSignal($signo);
|
||||
$this->inGracefulShutdown = true;
|
||||
}
|
||||
|
||||
final public function onNotifySignal($signo) {
|
||||
self::didCatchSignal($signo);
|
||||
$this->notifyReceived = true;
|
||||
$this->onNotify($signo);
|
||||
}
|
||||
|
||||
protected function onNotify($signo) {
|
||||
// This is a hook for subclasses.
|
||||
}
|
||||
|
||||
protected function willRun() {
|
||||
// This is a hook for subclasses.
|
||||
}
|
||||
|
||||
protected function didSetTraceMode() {
|
||||
// This is a hook for subclasses.
|
||||
}
|
||||
|
||||
final protected function log($message) {
|
||||
if ($this->verbose) {
|
||||
$daemon = get_class($this);
|
||||
fprintf(STDERR, "%s %s %s\n", '<VERB>', $daemon, $message);
|
||||
}
|
||||
}
|
||||
|
||||
private static function didCatchSignal($signo) {
|
||||
$signame = phutil_get_signal_name($signo);
|
||||
fprintf(
|
||||
STDERR,
|
||||
"%s Caught signal %s (%s).\n",
|
||||
'<SGNL>',
|
||||
$signo,
|
||||
$signame);
|
||||
}
|
||||
|
||||
|
||||
/* -( Communicating With the Overseer )------------------------------------ */
|
||||
|
||||
|
||||
private function beginStdoutCapture() {
|
||||
ob_start(array($this, 'didReceiveStdout'), 2);
|
||||
}
|
||||
|
||||
private function endStdoutCapture() {
|
||||
ob_end_flush();
|
||||
}
|
||||
|
||||
public function didReceiveStdout($data) {
|
||||
if (!strlen($data)) {
|
||||
return '';
|
||||
}
|
||||
|
||||
return $this->encodeOverseerMessage(self::MESSAGETYPE_STDOUT, $data);
|
||||
}
|
||||
|
||||
private function encodeOverseerMessage($type, $data) {
|
||||
$structure = array($type);
|
||||
|
||||
if ($data !== null) {
|
||||
$structure[] = $data;
|
||||
}
|
||||
|
||||
return json_encode($structure)."\n";
|
||||
}
|
||||
|
||||
private function emitOverseerMessage($type, $data) {
|
||||
$this->endStdoutCapture();
|
||||
echo $this->encodeOverseerMessage($type, $data);
|
||||
$this->beginStdoutCapture();
|
||||
}
|
||||
|
||||
public static function errorListener($event, $value, array $metadata) {
|
||||
// If the caller has redirected the error log to a file, PHP won't output
|
||||
// messages to stderr, so the overseer can't capture them. Install a
|
||||
// listener which just echoes errors to stderr, so the overseer is always
|
||||
// aware of errors.
|
||||
|
||||
$console = PhutilConsole::getConsole();
|
||||
$message = idx($metadata, 'default_message');
|
||||
|
||||
if ($message) {
|
||||
$console->writeErr("%s\n", $message);
|
||||
}
|
||||
if (idx($metadata, 'trace')) {
|
||||
$trace = PhutilErrorHandler::formatStacktrace($metadata['trace']);
|
||||
$console->writeErr("%s\n", $trace);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* -( Autoscaling )-------------------------------------------------------- */
|
||||
|
||||
|
||||
/**
|
||||
* Prepare to become busy. This may autoscale the pool up.
|
||||
*
|
||||
* This notifies the overseer that the daemon has become busy. If daemons
|
||||
* that are part of an autoscale pool are continuously busy for a prolonged
|
||||
* period of time, the overseer may scale up the pool.
|
||||
*
|
||||
* @return this
|
||||
* @task autoscale
|
||||
*/
|
||||
protected function willBeginWork() {
|
||||
if ($this->workState != self::WORKSTATE_BUSY) {
|
||||
$this->workState = self::WORKSTATE_BUSY;
|
||||
$this->idleSince = null;
|
||||
$this->emitOverseerMessage(self::MESSAGETYPE_BUSY, null);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Prepare to idle. This may autoscale the pool down.
|
||||
*
|
||||
* This notifies the overseer that the daemon is no longer busy. If daemons
|
||||
* that are part of an autoscale pool are idle for a prolonged period of
|
||||
* time, they may exit to scale the pool down.
|
||||
*
|
||||
* @return this
|
||||
* @task autoscale
|
||||
*/
|
||||
protected function willBeginIdle() {
|
||||
if ($this->workState != self::WORKSTATE_IDLE) {
|
||||
$this->workState = self::WORKSTATE_IDLE;
|
||||
$this->idleSince = time();
|
||||
$this->emitOverseerMessage(self::MESSAGETYPE_IDLE, null);
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
protected function getIdleDuration() {
|
||||
if (!$this->idleSince) {
|
||||
return null;
|
||||
}
|
||||
|
||||
$now = time();
|
||||
return ($now - $this->idleSince);
|
||||
}
|
||||
|
||||
}
|
506
src/infrastructure/daemon/PhutilDaemonHandle.php
Normal file
506
src/infrastructure/daemon/PhutilDaemonHandle.php
Normal file
|
@ -0,0 +1,506 @@
|
|||
<?php
|
||||
|
||||
final class PhutilDaemonHandle extends Phobject {
|
||||
|
||||
const EVENT_DID_LAUNCH = 'daemon.didLaunch';
|
||||
const EVENT_DID_LOG = 'daemon.didLogMessage';
|
||||
const EVENT_DID_HEARTBEAT = 'daemon.didHeartbeat';
|
||||
const EVENT_WILL_GRACEFUL = 'daemon.willGraceful';
|
||||
const EVENT_WILL_EXIT = 'daemon.willExit';
|
||||
|
||||
private $pool;
|
||||
private $properties;
|
||||
private $future;
|
||||
private $argv;
|
||||
|
||||
private $restartAt;
|
||||
private $busyEpoch;
|
||||
|
||||
private $pid;
|
||||
private $daemonID;
|
||||
private $deadline;
|
||||
private $heartbeat;
|
||||
private $stdoutBuffer;
|
||||
private $shouldRestart = true;
|
||||
private $shouldShutdown;
|
||||
private $hibernating = false;
|
||||
private $shouldSendExitEvent = false;
|
||||
|
||||
private function __construct() {
|
||||
// <empty>
|
||||
}
|
||||
|
||||
public static function newFromConfig(array $config) {
|
||||
PhutilTypeSpec::checkMap(
|
||||
$config,
|
||||
array(
|
||||
'class' => 'string',
|
||||
'argv' => 'optional list<string>',
|
||||
'load' => 'optional list<string>',
|
||||
'log' => 'optional string|null',
|
||||
'down' => 'optional int',
|
||||
));
|
||||
|
||||
$config = $config + array(
|
||||
'argv' => array(),
|
||||
'load' => array(),
|
||||
'log' => null,
|
||||
'down' => 15,
|
||||
);
|
||||
|
||||
$daemon = new self();
|
||||
$daemon->properties = $config;
|
||||
$daemon->daemonID = $daemon->generateDaemonID();
|
||||
|
||||
return $daemon;
|
||||
}
|
||||
|
||||
public function setDaemonPool(PhutilDaemonPool $daemon_pool) {
|
||||
$this->pool = $daemon_pool;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getDaemonPool() {
|
||||
return $this->pool;
|
||||
}
|
||||
|
||||
public function getBusyEpoch() {
|
||||
return $this->busyEpoch;
|
||||
}
|
||||
|
||||
public function getDaemonClass() {
|
||||
return $this->getProperty('class');
|
||||
}
|
||||
|
||||
private function getProperty($key) {
|
||||
return idx($this->properties, $key);
|
||||
}
|
||||
|
||||
public function setCommandLineArguments(array $arguments) {
|
||||
$this->argv = $arguments;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getCommandLineArguments() {
|
||||
return $this->argv;
|
||||
}
|
||||
|
||||
public function getDaemonArguments() {
|
||||
return $this->getProperty('argv');
|
||||
}
|
||||
|
||||
public function didLaunch() {
|
||||
$this->restartAt = time();
|
||||
$this->shouldSendExitEvent = true;
|
||||
|
||||
$this->dispatchEvent(
|
||||
self::EVENT_DID_LAUNCH,
|
||||
array(
|
||||
'argv' => $this->getCommandLineArguments(),
|
||||
'explicitArgv' => $this->getDaemonArguments(),
|
||||
));
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function isRunning() {
|
||||
return (bool)$this->future;
|
||||
}
|
||||
|
||||
public function isHibernating() {
|
||||
return
|
||||
!$this->isRunning() &&
|
||||
!$this->isDone() &&
|
||||
$this->hibernating;
|
||||
}
|
||||
|
||||
public function wakeFromHibernation() {
|
||||
if (!$this->isHibernating()) {
|
||||
return $this;
|
||||
}
|
||||
|
||||
$this->logMessage(
|
||||
'WAKE',
|
||||
pht(
|
||||
'Process is being awakened from hibernation.'));
|
||||
|
||||
$this->restartAt = time();
|
||||
$this->update();
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function isDone() {
|
||||
return (!$this->shouldRestart && !$this->isRunning());
|
||||
}
|
||||
|
||||
public function getFuture() {
|
||||
return $this->future;
|
||||
}
|
||||
|
||||
public function update() {
|
||||
if (!$this->isRunning()) {
|
||||
if (!$this->shouldRestart) {
|
||||
return;
|
||||
}
|
||||
if (!$this->restartAt || (time() < $this->restartAt)) {
|
||||
return;
|
||||
}
|
||||
if ($this->shouldShutdown) {
|
||||
return;
|
||||
}
|
||||
$this->startDaemonProcess();
|
||||
}
|
||||
|
||||
$future = $this->future;
|
||||
|
||||
$result = null;
|
||||
if ($future->isReady()) {
|
||||
$result = $future->resolve();
|
||||
}
|
||||
|
||||
list($stdout, $stderr) = $future->read();
|
||||
$future->discardBuffers();
|
||||
|
||||
if (strlen($stdout)) {
|
||||
$this->didReadStdout($stdout);
|
||||
}
|
||||
|
||||
$stderr = trim($stderr);
|
||||
if (strlen($stderr)) {
|
||||
foreach (phutil_split_lines($stderr, false) as $line) {
|
||||
$this->logMessage('STDE', $line);
|
||||
}
|
||||
}
|
||||
|
||||
if ($result !== null) {
|
||||
list($err) = $result;
|
||||
|
||||
if ($err) {
|
||||
$this->logMessage('FAIL', pht('Process exited with error %s.', $err));
|
||||
} else {
|
||||
$this->logMessage('DONE', pht('Process exited normally.'));
|
||||
}
|
||||
|
||||
$this->future = null;
|
||||
|
||||
if ($this->shouldShutdown) {
|
||||
$this->restartAt = null;
|
||||
} else {
|
||||
$this->scheduleRestart();
|
||||
}
|
||||
}
|
||||
|
||||
$this->updateHeartbeatEvent();
|
||||
$this->updateHangDetection();
|
||||
}
|
||||
|
||||
private function updateHeartbeatEvent() {
|
||||
if ($this->heartbeat > time()) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->heartbeat = time() + $this->getHeartbeatEventFrequency();
|
||||
$this->dispatchEvent(self::EVENT_DID_HEARTBEAT);
|
||||
}
|
||||
|
||||
private function updateHangDetection() {
|
||||
if (!$this->isRunning()) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (time() > $this->deadline) {
|
||||
$this->logMessage('HANG', pht('Hang detected. Restarting process.'));
|
||||
$this->annihilateProcessGroup();
|
||||
$this->scheduleRestart();
|
||||
}
|
||||
}
|
||||
|
||||
private function scheduleRestart() {
|
||||
// Wait a minimum of a few sceconds before restarting, but we may wait
|
||||
// longer if the daemon has initiated hibernation.
|
||||
$default_restart = time() + self::getWaitBeforeRestart();
|
||||
if ($default_restart >= $this->restartAt) {
|
||||
$this->restartAt = $default_restart;
|
||||
}
|
||||
|
||||
$this->logMessage(
|
||||
'WAIT',
|
||||
pht(
|
||||
'Waiting %s second(s) to restart process.',
|
||||
new PhutilNumber($this->restartAt - time())));
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a unique ID for this daemon.
|
||||
*
|
||||
* @return string A unique daemon ID.
|
||||
*/
|
||||
private function generateDaemonID() {
|
||||
return substr(getmypid().':'.Filesystem::readRandomCharacters(12), 0, 12);
|
||||
}
|
||||
|
||||
public function getDaemonID() {
|
||||
return $this->daemonID;
|
||||
}
|
||||
|
||||
public function getPID() {
|
||||
return $this->pid;
|
||||
}
|
||||
|
||||
private function getCaptureBufferSize() {
|
||||
return 65535;
|
||||
}
|
||||
|
||||
private function getRequiredHeartbeatFrequency() {
|
||||
return 86400;
|
||||
}
|
||||
|
||||
public static function getWaitBeforeRestart() {
|
||||
return 5;
|
||||
}
|
||||
|
||||
public static function getHeartbeatEventFrequency() {
|
||||
return 120;
|
||||
}
|
||||
|
||||
private function getKillDelay() {
|
||||
return 3;
|
||||
}
|
||||
|
||||
private function getDaemonCWD() {
|
||||
$root = dirname(phutil_get_library_root('phabricator'));
|
||||
return $root.'/scripts/daemon/exec/';
|
||||
}
|
||||
|
||||
private function newExecFuture() {
|
||||
$class = $this->getDaemonClass();
|
||||
$argv = $this->getCommandLineArguments();
|
||||
$buffer_size = $this->getCaptureBufferSize();
|
||||
|
||||
// NOTE: PHP implements proc_open() by running 'sh -c'. On most systems this
|
||||
// is bash, but on Ubuntu it's dash. When you proc_open() using bash, you
|
||||
// get one new process (the command you ran). When you proc_open() using
|
||||
// dash, you get two new processes: the command you ran and a parent
|
||||
// "dash -c" (or "sh -c") process. This means that the child process's PID
|
||||
// is actually the 'dash' PID, not the command's PID. To avoid this, use
|
||||
// 'exec' to replace the shell process with the real process; without this,
|
||||
// the child will call posix_getppid(), be given the pid of the 'sh -c'
|
||||
// process, and send it SIGUSR1 to keepalive which will terminate it
|
||||
// immediately. We also won't be able to do process group management because
|
||||
// the shell process won't properly posix_setsid() so the pgid of the child
|
||||
// won't be meaningful.
|
||||
|
||||
$config = $this->properties;
|
||||
unset($config['class']);
|
||||
$config = phutil_json_encode($config);
|
||||
|
||||
return id(new ExecFuture('exec ./exec_daemon.php %s %Ls', $class, $argv))
|
||||
->setCWD($this->getDaemonCWD())
|
||||
->setStdoutSizeLimit($buffer_size)
|
||||
->setStderrSizeLimit($buffer_size)
|
||||
->write($config);
|
||||
}
|
||||
|
||||
/**
|
||||
* Dispatch an event to event listeners.
|
||||
*
|
||||
* @param string Event type.
|
||||
* @param dict Event parameters.
|
||||
* @return void
|
||||
*/
|
||||
private function dispatchEvent($type, array $params = array()) {
|
||||
$data = array(
|
||||
'id' => $this->getDaemonID(),
|
||||
'daemonClass' => $this->getDaemonClass(),
|
||||
'childPID' => $this->getPID(),
|
||||
) + $params;
|
||||
|
||||
$event = new PhutilEvent($type, $data);
|
||||
|
||||
try {
|
||||
PhutilEventEngine::dispatchEvent($event);
|
||||
} catch (Exception $ex) {
|
||||
phlog($ex);
|
||||
}
|
||||
}
|
||||
|
||||
private function annihilateProcessGroup() {
|
||||
$pid = $this->getPID();
|
||||
|
||||
$pgid = posix_getpgid($pid);
|
||||
if ($pid && $pgid) {
|
||||
posix_kill(-$pgid, SIGTERM);
|
||||
sleep($this->getKillDelay());
|
||||
posix_kill(-$pgid, SIGKILL);
|
||||
$this->pid = null;
|
||||
}
|
||||
}
|
||||
|
||||
private function startDaemonProcess() {
|
||||
$this->logMessage('INIT', pht('Starting process.'));
|
||||
|
||||
$this->deadline = time() + $this->getRequiredHeartbeatFrequency();
|
||||
$this->heartbeat = time() + self::getHeartbeatEventFrequency();
|
||||
$this->stdoutBuffer = '';
|
||||
$this->hibernating = false;
|
||||
|
||||
$this->future = $this->newExecFuture();
|
||||
$this->future->start();
|
||||
|
||||
$this->pid = $this->future->getPID();
|
||||
}
|
||||
|
||||
private function didReadStdout($data) {
|
||||
$this->stdoutBuffer .= $data;
|
||||
while (true) {
|
||||
$pos = strpos($this->stdoutBuffer, "\n");
|
||||
if ($pos === false) {
|
||||
break;
|
||||
}
|
||||
$message = substr($this->stdoutBuffer, 0, $pos);
|
||||
$this->stdoutBuffer = substr($this->stdoutBuffer, $pos + 1);
|
||||
|
||||
try {
|
||||
$structure = phutil_json_decode($message);
|
||||
} catch (PhutilJSONParserException $ex) {
|
||||
$structure = array();
|
||||
}
|
||||
|
||||
switch (idx($structure, 0)) {
|
||||
case PhutilDaemon::MESSAGETYPE_STDOUT:
|
||||
$this->logMessage('STDO', idx($structure, 1));
|
||||
break;
|
||||
case PhutilDaemon::MESSAGETYPE_HEARTBEAT:
|
||||
$this->deadline = time() + $this->getRequiredHeartbeatFrequency();
|
||||
break;
|
||||
case PhutilDaemon::MESSAGETYPE_BUSY:
|
||||
if (!$this->busyEpoch) {
|
||||
$this->busyEpoch = time();
|
||||
}
|
||||
break;
|
||||
case PhutilDaemon::MESSAGETYPE_IDLE:
|
||||
$this->busyEpoch = null;
|
||||
break;
|
||||
case PhutilDaemon::MESSAGETYPE_DOWN:
|
||||
// The daemon is exiting because it doesn't have enough work and it
|
||||
// is trying to scale the pool down. We should not restart it.
|
||||
$this->shouldRestart = false;
|
||||
$this->shouldShutdown = true;
|
||||
break;
|
||||
case PhutilDaemon::MESSAGETYPE_HIBERNATE:
|
||||
$config = idx($structure, 1);
|
||||
$duration = (int)idx($config, 'duration', 0);
|
||||
$this->restartAt = time() + $duration;
|
||||
$this->hibernating = true;
|
||||
$this->busyEpoch = null;
|
||||
$this->logMessage(
|
||||
'ZZZZ',
|
||||
pht(
|
||||
'Process is preparing to hibernate for %s second(s).',
|
||||
new PhutilNumber($duration)));
|
||||
break;
|
||||
default:
|
||||
// If we can't parse this or it isn't a message we understand, just
|
||||
// emit the raw message.
|
||||
$this->logMessage('STDO', pht('<Malformed> %s', $message));
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function didReceiveNotifySignal($signo) {
|
||||
$pid = $this->getPID();
|
||||
if ($pid) {
|
||||
posix_kill($pid, $signo);
|
||||
}
|
||||
}
|
||||
|
||||
public function didReceiveReloadSignal($signo) {
|
||||
$signame = phutil_get_signal_name($signo);
|
||||
if ($signame) {
|
||||
$sigmsg = pht(
|
||||
'Reloading in response to signal %d (%s).',
|
||||
$signo,
|
||||
$signame);
|
||||
} else {
|
||||
$sigmsg = pht(
|
||||
'Reloading in response to signal %d.',
|
||||
$signo);
|
||||
}
|
||||
|
||||
$this->logMessage('RELO', $sigmsg, $signo);
|
||||
|
||||
// This signal means "stop the current process gracefully, then launch
|
||||
// a new identical process once it exits". This can be used to update
|
||||
// daemons after code changes (the new processes will run the new code)
|
||||
// without aborting any running tasks.
|
||||
|
||||
// We SIGINT the daemon but don't set the shutdown flag, so it will
|
||||
// naturally be restarted after it exits, as though it had exited after an
|
||||
// unhandled exception.
|
||||
|
||||
posix_kill($this->getPID(), SIGINT);
|
||||
}
|
||||
|
||||
public function didReceiveGracefulSignal($signo) {
|
||||
$this->shouldShutdown = true;
|
||||
$this->shouldRestart = false;
|
||||
|
||||
$signame = phutil_get_signal_name($signo);
|
||||
if ($signame) {
|
||||
$sigmsg = pht(
|
||||
'Graceful shutdown in response to signal %d (%s).',
|
||||
$signo,
|
||||
$signame);
|
||||
} else {
|
||||
$sigmsg = pht(
|
||||
'Graceful shutdown in response to signal %d.',
|
||||
$signo);
|
||||
}
|
||||
|
||||
$this->logMessage('DONE', $sigmsg, $signo);
|
||||
|
||||
posix_kill($this->getPID(), SIGINT);
|
||||
}
|
||||
|
||||
public function didReceiveTerminateSignal($signo) {
|
||||
$this->shouldShutdown = true;
|
||||
$this->shouldRestart = false;
|
||||
|
||||
$signame = phutil_get_signal_name($signo);
|
||||
if ($signame) {
|
||||
$sigmsg = pht(
|
||||
'Shutting down in response to signal %s (%s).',
|
||||
$signo,
|
||||
$signame);
|
||||
} else {
|
||||
$sigmsg = pht('Shutting down in response to signal %s.', $signo);
|
||||
}
|
||||
|
||||
$this->logMessage('EXIT', $sigmsg, $signo);
|
||||
$this->annihilateProcessGroup();
|
||||
}
|
||||
|
||||
private function logMessage($type, $message, $context = null) {
|
||||
$this->getDaemonPool()->logMessage($type, $message, $context);
|
||||
|
||||
$this->dispatchEvent(
|
||||
self::EVENT_DID_LOG,
|
||||
array(
|
||||
'type' => $type,
|
||||
'message' => $message,
|
||||
'context' => $context,
|
||||
));
|
||||
}
|
||||
|
||||
public function didExit() {
|
||||
if ($this->shouldSendExitEvent) {
|
||||
$this->dispatchEvent(self::EVENT_WILL_EXIT);
|
||||
$this->shouldSendExitEvent = false;
|
||||
}
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
}
|
405
src/infrastructure/daemon/PhutilDaemonOverseer.php
Normal file
405
src/infrastructure/daemon/PhutilDaemonOverseer.php
Normal file
|
@ -0,0 +1,405 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Oversees a daemon and restarts it if it fails.
|
||||
*
|
||||
* @task signals Signal Handling
|
||||
*/
|
||||
final class PhutilDaemonOverseer extends Phobject {
|
||||
|
||||
private $argv;
|
||||
private static $instance;
|
||||
|
||||
private $config;
|
||||
private $pools = array();
|
||||
private $traceMode;
|
||||
private $traceMemory;
|
||||
private $daemonize;
|
||||
private $log;
|
||||
private $libraries = array();
|
||||
private $modules = array();
|
||||
private $verbose;
|
||||
private $startEpoch;
|
||||
private $autoscale = array();
|
||||
private $autoscaleConfig = array();
|
||||
|
||||
const SIGNAL_NOTIFY = 'signal/notify';
|
||||
const SIGNAL_RELOAD = 'signal/reload';
|
||||
const SIGNAL_GRACEFUL = 'signal/graceful';
|
||||
const SIGNAL_TERMINATE = 'signal/terminate';
|
||||
|
||||
private $err = 0;
|
||||
private $inAbruptShutdown;
|
||||
private $inGracefulShutdown;
|
||||
|
||||
public function __construct(array $argv) {
|
||||
PhutilServiceProfiler::getInstance()->enableDiscardMode();
|
||||
|
||||
$args = new PhutilArgumentParser($argv);
|
||||
$args->setTagline(pht('daemon overseer'));
|
||||
$args->setSynopsis(<<<EOHELP
|
||||
**launch_daemon.php** [__options__] __daemon__
|
||||
Launch and oversee an instance of __daemon__.
|
||||
EOHELP
|
||||
);
|
||||
$args->parseStandardArguments();
|
||||
$args->parse(
|
||||
array(
|
||||
array(
|
||||
'name' => 'trace-memory',
|
||||
'help' => pht('Enable debug memory tracing.'),
|
||||
),
|
||||
array(
|
||||
'name' => 'verbose',
|
||||
'help' => pht('Enable verbose activity logging.'),
|
||||
),
|
||||
array(
|
||||
'name' => 'label',
|
||||
'short' => 'l',
|
||||
'param' => 'label',
|
||||
'help' => pht(
|
||||
'Optional process label. Makes "%s" nicer, no behavioral effects.',
|
||||
'ps'),
|
||||
),
|
||||
));
|
||||
$argv = array();
|
||||
|
||||
if ($args->getArg('trace')) {
|
||||
$this->traceMode = true;
|
||||
$argv[] = '--trace';
|
||||
}
|
||||
|
||||
if ($args->getArg('trace-memory')) {
|
||||
$this->traceMode = true;
|
||||
$this->traceMemory = true;
|
||||
$argv[] = '--trace-memory';
|
||||
}
|
||||
$verbose = $args->getArg('verbose');
|
||||
if ($verbose) {
|
||||
$this->verbose = true;
|
||||
$argv[] = '--verbose';
|
||||
}
|
||||
|
||||
$label = $args->getArg('label');
|
||||
if ($label) {
|
||||
$argv[] = '-l';
|
||||
$argv[] = $label;
|
||||
}
|
||||
|
||||
$this->argv = $argv;
|
||||
|
||||
if (function_exists('posix_isatty') && posix_isatty(STDIN)) {
|
||||
fprintf(STDERR, pht('Reading daemon configuration from stdin...')."\n");
|
||||
}
|
||||
$config = @file_get_contents('php://stdin');
|
||||
$config = id(new PhutilJSONParser())->parse($config);
|
||||
|
||||
$this->libraries = idx($config, 'load');
|
||||
$this->log = idx($config, 'log');
|
||||
$this->daemonize = idx($config, 'daemonize');
|
||||
|
||||
$this->config = $config;
|
||||
|
||||
if (self::$instance) {
|
||||
throw new Exception(
|
||||
pht('You may not instantiate more than one Overseer per process.'));
|
||||
}
|
||||
|
||||
self::$instance = $this;
|
||||
|
||||
$this->startEpoch = time();
|
||||
|
||||
if (!idx($config, 'daemons')) {
|
||||
throw new PhutilArgumentUsageException(
|
||||
pht('You must specify at least one daemon to start!'));
|
||||
}
|
||||
|
||||
if ($this->log) {
|
||||
// NOTE: Now that we're committed to daemonizing, redirect the error
|
||||
// log if we have a `--log` parameter. Do this at the last moment
|
||||
// so as many setup issues as possible are surfaced.
|
||||
ini_set('error_log', $this->log);
|
||||
}
|
||||
|
||||
if ($this->daemonize) {
|
||||
// We need to get rid of these or the daemon will hang when we TERM it
|
||||
// waiting for something to read the buffers. TODO: Learn how unix works.
|
||||
fclose(STDOUT);
|
||||
fclose(STDERR);
|
||||
ob_start();
|
||||
|
||||
$pid = pcntl_fork();
|
||||
if ($pid === -1) {
|
||||
throw new Exception(pht('Unable to fork!'));
|
||||
} else if ($pid) {
|
||||
exit(0);
|
||||
}
|
||||
|
||||
$sid = posix_setsid();
|
||||
if ($sid <= 0) {
|
||||
throw new Exception(pht('Failed to create new process session!'));
|
||||
}
|
||||
}
|
||||
|
||||
$this->logMessage(
|
||||
'OVER',
|
||||
pht(
|
||||
'Started new daemon overseer (with PID "%s").',
|
||||
getmypid()));
|
||||
|
||||
$this->modules = PhutilDaemonOverseerModule::getAllModules();
|
||||
|
||||
$this->installSignalHandlers();
|
||||
}
|
||||
|
||||
public function addLibrary($library) {
|
||||
$this->libraries[] = $library;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function run() {
|
||||
$this->createDaemonPools();
|
||||
|
||||
while (true) {
|
||||
if ($this->shouldReloadDaemons()) {
|
||||
$this->didReceiveSignal(SIGHUP);
|
||||
}
|
||||
|
||||
$futures = array();
|
||||
|
||||
$running_pools = false;
|
||||
foreach ($this->getDaemonPools() as $pool) {
|
||||
$pool->updatePool();
|
||||
|
||||
if (!$this->shouldShutdown()) {
|
||||
if ($pool->isHibernating()) {
|
||||
if ($this->shouldWakePool($pool)) {
|
||||
$pool->wakeFromHibernation();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
foreach ($pool->getFutures() as $future) {
|
||||
$futures[] = $future;
|
||||
}
|
||||
|
||||
if ($pool->getDaemons()) {
|
||||
$running_pools = true;
|
||||
}
|
||||
}
|
||||
|
||||
$this->updateMemory();
|
||||
|
||||
$this->waitForDaemonFutures($futures);
|
||||
|
||||
if (!$futures && !$running_pools) {
|
||||
if ($this->shouldShutdown()) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
exit($this->err);
|
||||
}
|
||||
|
||||
|
||||
private function waitForDaemonFutures(array $futures) {
|
||||
assert_instances_of($futures, 'ExecFuture');
|
||||
|
||||
if ($futures) {
|
||||
// TODO: This only wakes if any daemons actually exit. It would be a bit
|
||||
// cleaner to wait on any I/O with Channels.
|
||||
$iter = id(new FutureIterator($futures))
|
||||
->setUpdateInterval(1);
|
||||
foreach ($iter as $future) {
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
if (!$this->shouldShutdown()) {
|
||||
sleep(1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private function createDaemonPools() {
|
||||
$configs = $this->config['daemons'];
|
||||
|
||||
$forced_options = array(
|
||||
'load' => $this->libraries,
|
||||
'log' => $this->log,
|
||||
);
|
||||
|
||||
foreach ($configs as $config) {
|
||||
$config = $forced_options + $config;
|
||||
|
||||
$pool = PhutilDaemonPool::newFromConfig($config)
|
||||
->setOverseer($this)
|
||||
->setCommandLineArguments($this->argv);
|
||||
|
||||
$this->pools[] = $pool;
|
||||
}
|
||||
}
|
||||
|
||||
private function getDaemonPools() {
|
||||
return $this->pools;
|
||||
}
|
||||
|
||||
private function updateMemory() {
|
||||
if (!$this->traceMemory) {
|
||||
return;
|
||||
}
|
||||
|
||||
$this->logMessage(
|
||||
'RAMS',
|
||||
pht(
|
||||
'Overseer Memory Usage: %s KB',
|
||||
new PhutilNumber(memory_get_usage() / 1024, 1)));
|
||||
}
|
||||
|
||||
public function logMessage($type, $message, $context = null) {
|
||||
$always_log = false;
|
||||
switch ($type) {
|
||||
case 'OVER':
|
||||
case 'SGNL':
|
||||
case 'PIDF':
|
||||
$always_log = true;
|
||||
break;
|
||||
}
|
||||
|
||||
if ($always_log || $this->traceMode || $this->verbose) {
|
||||
error_log(date('Y-m-d g:i:s A').' ['.$type.'] '.$message);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* -( Signal Handling )---------------------------------------------------- */
|
||||
|
||||
|
||||
/**
|
||||
* @task signals
|
||||
*/
|
||||
private function installSignalHandlers() {
|
||||
$signals = array(
|
||||
SIGUSR2,
|
||||
SIGHUP,
|
||||
SIGINT,
|
||||
SIGTERM,
|
||||
);
|
||||
|
||||
foreach ($signals as $signal) {
|
||||
pcntl_signal($signal, array($this, 'didReceiveSignal'));
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* @task signals
|
||||
*/
|
||||
public function didReceiveSignal($signo) {
|
||||
$this->logMessage(
|
||||
'SGNL',
|
||||
pht(
|
||||
'Overseer ("%d") received signal %d ("%s").',
|
||||
getmypid(),
|
||||
$signo,
|
||||
phutil_get_signal_name($signo)));
|
||||
|
||||
switch ($signo) {
|
||||
case SIGUSR2:
|
||||
$signal_type = self::SIGNAL_NOTIFY;
|
||||
break;
|
||||
case SIGHUP:
|
||||
$signal_type = self::SIGNAL_RELOAD;
|
||||
break;
|
||||
case SIGINT:
|
||||
// If we receive SIGINT more than once, interpret it like SIGTERM.
|
||||
if ($this->inGracefulShutdown) {
|
||||
return $this->didReceiveSignal(SIGTERM);
|
||||
}
|
||||
|
||||
$this->inGracefulShutdown = true;
|
||||
$signal_type = self::SIGNAL_GRACEFUL;
|
||||
break;
|
||||
case SIGTERM:
|
||||
// If we receive SIGTERM more than once, terminate abruptly.
|
||||
$this->err = 128 + $signo;
|
||||
if ($this->inAbruptShutdown) {
|
||||
exit($this->err);
|
||||
}
|
||||
|
||||
$this->inAbruptShutdown = true;
|
||||
$signal_type = self::SIGNAL_TERMINATE;
|
||||
break;
|
||||
default:
|
||||
throw new Exception(
|
||||
pht(
|
||||
'Signal handler called with unknown signal type ("%d")!',
|
||||
$signo));
|
||||
}
|
||||
|
||||
foreach ($this->getDaemonPools() as $pool) {
|
||||
$pool->didReceiveSignal($signal_type, $signo);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/* -( Daemon Modules )----------------------------------------------------- */
|
||||
|
||||
|
||||
private function getModules() {
|
||||
return $this->modules;
|
||||
}
|
||||
|
||||
private function shouldReloadDaemons() {
|
||||
$modules = $this->getModules();
|
||||
|
||||
$should_reload = false;
|
||||
foreach ($modules as $module) {
|
||||
try {
|
||||
// NOTE: Even if one module tells us to reload, we call the method on
|
||||
// each module anyway to make calls a little more predictable.
|
||||
|
||||
if ($module->shouldReloadDaemons()) {
|
||||
$this->logMessage(
|
||||
'RELO',
|
||||
pht(
|
||||
'Reloading daemons (triggered by overseer module "%s").',
|
||||
get_class($module)));
|
||||
$should_reload = true;
|
||||
}
|
||||
} catch (Exception $ex) {
|
||||
phlog($ex);
|
||||
}
|
||||
}
|
||||
|
||||
return $should_reload;
|
||||
}
|
||||
|
||||
private function shouldWakePool(PhutilDaemonPool $pool) {
|
||||
$modules = $this->getModules();
|
||||
|
||||
$should_wake = false;
|
||||
foreach ($modules as $module) {
|
||||
try {
|
||||
if ($module->shouldWakePool($pool)) {
|
||||
$this->logMessage(
|
||||
'WAKE',
|
||||
pht(
|
||||
'Waking pool "%s" (triggered by overseer module "%s").',
|
||||
$pool->getPoolLabel(),
|
||||
get_class($module)));
|
||||
$should_wake = true;
|
||||
}
|
||||
} catch (Exception $ex) {
|
||||
phlog($ex);
|
||||
}
|
||||
}
|
||||
|
||||
return $should_wake;
|
||||
}
|
||||
|
||||
private function shouldShutdown() {
|
||||
return $this->inGracefulShutdown || $this->inAbruptShutdown;
|
||||
}
|
||||
|
||||
}
|
71
src/infrastructure/daemon/PhutilDaemonOverseerModule.php
Normal file
71
src/infrastructure/daemon/PhutilDaemonOverseerModule.php
Normal file
|
@ -0,0 +1,71 @@
|
|||
<?php
|
||||
|
||||
/**
|
||||
* Overseer modules allow daemons to be externally influenced.
|
||||
*
|
||||
* See @{class:PhabricatorDaemonOverseerModule} for a concrete example.
|
||||
*/
|
||||
abstract class PhutilDaemonOverseerModule extends Phobject {
|
||||
|
||||
private $throttles = array();
|
||||
|
||||
|
||||
/**
|
||||
* This method is used to indicate to the overseer that daemons should reload.
|
||||
*
|
||||
* @return bool True if the daemons should reload, otherwise false.
|
||||
*/
|
||||
public function shouldReloadDaemons() {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Should a hibernating daemon pool be awoken immediately?
|
||||
*
|
||||
* @return bool True to awaken the pool immediately.
|
||||
*/
|
||||
public function shouldWakePool(PhutilDaemonPool $pool) {
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
public static function getAllModules() {
|
||||
return id(new PhutilClassMapQuery())
|
||||
->setAncestorClass(__CLASS__)
|
||||
->execute();
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Throttle checks from executing too often.
|
||||
*
|
||||
* If you throttle a check like this, it will only execute once every 2.5
|
||||
* seconds:
|
||||
*
|
||||
* if ($this->shouldThrottle('some.check', 2.5)) {
|
||||
* return;
|
||||
* }
|
||||
*
|
||||
* @param string Throttle key.
|
||||
* @param float Duration in seconds.
|
||||
* @return bool True to throttle the check.
|
||||
*/
|
||||
protected function shouldThrottle($name, $duration) {
|
||||
$throttle = idx($this->throttles, $name, 0);
|
||||
$now = microtime(true);
|
||||
|
||||
// If not enough time has elapsed, throttle the check.
|
||||
$elapsed = ($now - $throttle);
|
||||
if ($elapsed < $duration) {
|
||||
return true;
|
||||
}
|
||||
|
||||
// Otherwise, mark the current time as the last time we ran the check,
|
||||
// then let it continue.
|
||||
$this->throttles[$name] = $now;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
}
|
360
src/infrastructure/daemon/PhutilDaemonPool.php
Normal file
360
src/infrastructure/daemon/PhutilDaemonPool.php
Normal file
|
@ -0,0 +1,360 @@
|
|||
<?php
|
||||
|
||||
final class PhutilDaemonPool extends Phobject {
|
||||
|
||||
private $properties = array();
|
||||
private $commandLineArguments;
|
||||
|
||||
private $overseer;
|
||||
private $daemons = array();
|
||||
private $argv;
|
||||
|
||||
private $lastAutoscaleUpdate;
|
||||
private $inShutdown;
|
||||
|
||||
private function __construct() {
|
||||
// <empty>
|
||||
}
|
||||
|
||||
public static function newFromConfig(array $config) {
|
||||
PhutilTypeSpec::checkMap(
|
||||
$config,
|
||||
array(
|
||||
'class' => 'string',
|
||||
'label' => 'string',
|
||||
'argv' => 'optional list<string>',
|
||||
'load' => 'optional list<string>',
|
||||
'log' => 'optional string|null',
|
||||
'pool' => 'optional int',
|
||||
'up' => 'optional int',
|
||||
'down' => 'optional int',
|
||||
'reserve' => 'optional int|float',
|
||||
));
|
||||
|
||||
$config = $config + array(
|
||||
'argv' => array(),
|
||||
'load' => array(),
|
||||
'log' => null,
|
||||
'pool' => 1,
|
||||
'up' => 2,
|
||||
'down' => 15,
|
||||
'reserve' => 0,
|
||||
);
|
||||
|
||||
$pool = new self();
|
||||
$pool->properties = $config;
|
||||
|
||||
return $pool;
|
||||
}
|
||||
|
||||
public function setOverseer(PhutilDaemonOverseer $overseer) {
|
||||
$this->overseer = $overseer;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getOverseer() {
|
||||
return $this->overseer;
|
||||
}
|
||||
|
||||
public function setCommandLineArguments(array $arguments) {
|
||||
$this->commandLineArguments = $arguments;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getCommandLineArguments() {
|
||||
return $this->commandLineArguments;
|
||||
}
|
||||
|
||||
private function shouldShutdown() {
|
||||
return $this->inShutdown;
|
||||
}
|
||||
|
||||
private function newDaemon() {
|
||||
$config = $this->properties;
|
||||
|
||||
if (count($this->daemons)) {
|
||||
$down_duration = $this->getPoolScaledownDuration();
|
||||
} else {
|
||||
// TODO: For now, never scale pools down to 0.
|
||||
$down_duration = 0;
|
||||
}
|
||||
|
||||
$forced_config = array(
|
||||
'down' => $down_duration,
|
||||
);
|
||||
|
||||
$config = $forced_config + $config;
|
||||
|
||||
$config = array_select_keys(
|
||||
$config,
|
||||
array(
|
||||
'class',
|
||||
'log',
|
||||
'load',
|
||||
'argv',
|
||||
'down',
|
||||
));
|
||||
|
||||
$daemon = PhutilDaemonHandle::newFromConfig($config)
|
||||
->setDaemonPool($this)
|
||||
->setCommandLineArguments($this->getCommandLineArguments());
|
||||
|
||||
$daemon_id = $daemon->getDaemonID();
|
||||
$this->daemons[$daemon_id] = $daemon;
|
||||
|
||||
$daemon->didLaunch();
|
||||
|
||||
return $daemon;
|
||||
}
|
||||
|
||||
public function getDaemons() {
|
||||
return $this->daemons;
|
||||
}
|
||||
|
||||
public function getFutures() {
|
||||
$futures = array();
|
||||
foreach ($this->getDaemons() as $daemon) {
|
||||
$future = $daemon->getFuture();
|
||||
if ($future) {
|
||||
$futures[] = $future;
|
||||
}
|
||||
}
|
||||
|
||||
return $futures;
|
||||
}
|
||||
|
||||
public function didReceiveSignal($signal, $signo) {
|
||||
switch ($signal) {
|
||||
case PhutilDaemonOverseer::SIGNAL_GRACEFUL:
|
||||
case PhutilDaemonOverseer::SIGNAL_TERMINATE:
|
||||
$this->inShutdown = true;
|
||||
break;
|
||||
}
|
||||
|
||||
foreach ($this->getDaemons() as $daemon) {
|
||||
switch ($signal) {
|
||||
case PhutilDaemonOverseer::SIGNAL_NOTIFY:
|
||||
$daemon->didReceiveNotifySignal($signo);
|
||||
break;
|
||||
case PhutilDaemonOverseer::SIGNAL_RELOAD:
|
||||
$daemon->didReceiveReloadSignal($signo);
|
||||
break;
|
||||
case PhutilDaemonOverseer::SIGNAL_GRACEFUL:
|
||||
$daemon->didReceiveGracefulSignal($signo);
|
||||
break;
|
||||
case PhutilDaemonOverseer::SIGNAL_TERMINATE:
|
||||
$daemon->didReceiveTerminateSignal($signo);
|
||||
break;
|
||||
default:
|
||||
throw new Exception(
|
||||
pht(
|
||||
'Unknown signal "%s" ("%d").',
|
||||
$signal,
|
||||
$signo));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function getPoolLabel() {
|
||||
return $this->getPoolProperty('label');
|
||||
}
|
||||
|
||||
public function getPoolMaximumSize() {
|
||||
return $this->getPoolProperty('pool');
|
||||
}
|
||||
|
||||
public function getPoolScaleupDuration() {
|
||||
return $this->getPoolProperty('up');
|
||||
}
|
||||
|
||||
public function getPoolScaledownDuration() {
|
||||
return $this->getPoolProperty('down');
|
||||
}
|
||||
|
||||
public function getPoolMemoryReserve() {
|
||||
return $this->getPoolProperty('reserve');
|
||||
}
|
||||
|
||||
public function getPoolDaemonClass() {
|
||||
return $this->getPoolProperty('class');
|
||||
}
|
||||
|
||||
private function getPoolProperty($key) {
|
||||
return idx($this->properties, $key);
|
||||
}
|
||||
|
||||
public function updatePool() {
|
||||
$daemons = $this->getDaemons();
|
||||
|
||||
foreach ($daemons as $key => $daemon) {
|
||||
$daemon->update();
|
||||
|
||||
if ($daemon->isDone()) {
|
||||
$daemon->didExit();
|
||||
|
||||
unset($this->daemons[$key]);
|
||||
|
||||
if ($this->shouldShutdown()) {
|
||||
$this->logMessage(
|
||||
'DOWN',
|
||||
pht(
|
||||
'Pool "%s" is exiting, with %s daemon(s) remaining.',
|
||||
$this->getPoolLabel(),
|
||||
new PhutilNumber(count($this->daemons))));
|
||||
} else {
|
||||
$this->logMessage(
|
||||
'POOL',
|
||||
pht(
|
||||
'Autoscale pool "%s" scaled down to %s daemon(s).',
|
||||
$this->getPoolLabel(),
|
||||
new PhutilNumber(count($this->daemons))));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$this->updateAutoscale();
|
||||
}
|
||||
|
||||
public function isHibernating() {
|
||||
foreach ($this->getDaemons() as $daemon) {
|
||||
if (!$daemon->isHibernating()) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public function wakeFromHibernation() {
|
||||
if (!$this->isHibernating()) {
|
||||
return $this;
|
||||
}
|
||||
|
||||
$this->logMessage(
|
||||
'WAKE',
|
||||
pht(
|
||||
'Autoscale pool "%s" is being awakened from hibernation.',
|
||||
$this->getPoolLabel()));
|
||||
|
||||
$did_wake_daemons = false;
|
||||
foreach ($this->getDaemons() as $daemon) {
|
||||
if ($daemon->isHibernating()) {
|
||||
$daemon->wakeFromHibernation();
|
||||
$did_wake_daemons = true;
|
||||
}
|
||||
}
|
||||
|
||||
if (!$did_wake_daemons) {
|
||||
// TODO: Pools currently can't scale down to 0 daemons, but we should
|
||||
// scale up immediately here once they can.
|
||||
}
|
||||
|
||||
$this->updatePool();
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
private function updateAutoscale() {
|
||||
if ($this->shouldShutdown()) {
|
||||
return;
|
||||
}
|
||||
|
||||
// Don't try to autoscale more than once per second. This mostly stops the
|
||||
// logs from getting flooded in verbose mode.
|
||||
$now = time();
|
||||
if ($this->lastAutoscaleUpdate >= $now) {
|
||||
return;
|
||||
}
|
||||
$this->lastAutoscaleUpdate = $now;
|
||||
|
||||
$daemons = $this->getDaemons();
|
||||
|
||||
// If this pool is already at the maximum size, we can't launch any new
|
||||
// daemons.
|
||||
$max_size = $this->getPoolMaximumSize();
|
||||
if (count($daemons) >= $max_size) {
|
||||
$this->logMessage(
|
||||
'POOL',
|
||||
pht(
|
||||
'Autoscale pool "%s" already at maximum size (%s of %s).',
|
||||
$this->getPoolLabel(),
|
||||
new PhutilNumber(count($daemons)),
|
||||
new PhutilNumber($max_size)));
|
||||
return;
|
||||
}
|
||||
|
||||
$scaleup_duration = $this->getPoolScaleupDuration();
|
||||
|
||||
foreach ($daemons as $daemon) {
|
||||
$busy_epoch = $daemon->getBusyEpoch();
|
||||
// If any daemons haven't started work yet, don't scale the pool up.
|
||||
if (!$busy_epoch) {
|
||||
$this->logMessage(
|
||||
'POOL',
|
||||
pht(
|
||||
'Autoscale pool "%s" has an idle daemon, declining to scale.',
|
||||
$this->getPoolLabel()));
|
||||
return;
|
||||
}
|
||||
|
||||
// If any daemons started work very recently, wait a little while
|
||||
// to scale the pool up.
|
||||
$busy_for = ($now - $busy_epoch);
|
||||
if ($busy_for < $scaleup_duration) {
|
||||
$this->logMessage(
|
||||
'POOL',
|
||||
pht(
|
||||
'Autoscale pool "%s" has not been busy long enough to scale up '.
|
||||
'(busy for %s of %s seconds).',
|
||||
$this->getPoolLabel(),
|
||||
new PhutilNumber($busy_for),
|
||||
new PhutilNumber($scaleup_duration)));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
// If we have a configured memory reserve for this pool, it tells us that
|
||||
// we should not scale up unless there's at least that much memory left
|
||||
// on the system (for example, a reserve of 0.25 means that 25% of system
|
||||
// memory must be free to autoscale).
|
||||
|
||||
// Note that the first daemon is exempt: we'll always launch at least one
|
||||
// daemon, regardless of any memory reservation.
|
||||
if (count($daemons)) {
|
||||
$reserve = $this->getPoolMemoryReserve();
|
||||
if ($reserve) {
|
||||
// On some systems this may be slightly more expensive than other
|
||||
// checks, so we only do it once we're prepared to scale up.
|
||||
$memory = PhutilSystem::getSystemMemoryInformation();
|
||||
$free_ratio = ($memory['free'] / $memory['total']);
|
||||
|
||||
// If we don't have enough free memory, don't scale.
|
||||
if ($free_ratio <= $reserve) {
|
||||
$this->logMessage(
|
||||
'POOL',
|
||||
pht(
|
||||
'Autoscale pool "%s" does not have enough free memory to '.
|
||||
'scale up (%s free of %s reserved).',
|
||||
$this->getPoolLabel(),
|
||||
new PhutilNumber($free_ratio, 3),
|
||||
new PhutilNumber($reserve, 3)));
|
||||
return;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$this->logMessage(
|
||||
'AUTO',
|
||||
pht(
|
||||
'Scaling pool "%s" up to %s daemon(s).',
|
||||
$this->getPoolLabel(),
|
||||
new PhutilNumber(count($daemons) + 1)));
|
||||
|
||||
$this->newDaemon();
|
||||
}
|
||||
|
||||
public function logMessage($type, $message, $context = null) {
|
||||
return $this->getOverseer()->logMessage($type, $message, $context);
|
||||
}
|
||||
|
||||
}
|
|
@ -19,7 +19,6 @@ final class AphrontStackTraceView extends AphrontView {
|
|||
|
||||
$callsigns = array(
|
||||
'arcanist' => 'ARC',
|
||||
'phutil' => 'PHU',
|
||||
'phabricator' => 'P',
|
||||
);
|
||||
|
||||
|
|
|
@ -205,16 +205,13 @@ final class PhabricatorStartup {
|
|||
'include_path',
|
||||
$libraries_root.PATH_SEPARATOR.ini_get('include_path'));
|
||||
|
||||
@include_once $root.'libphutil/src/__phutil_library_init__.php';
|
||||
if (!@constant('__LIBPHUTIL__')) {
|
||||
$ok = @include_once $root.'arcanist/src/init/init-library.php';
|
||||
if (!$ok) {
|
||||
self::didFatal(
|
||||
"Unable to load libphutil. Put libphutil/ next to phabricator/, or ".
|
||||
"update your PHP 'include_path' to include the parent directory of ".
|
||||
"libphutil/.");
|
||||
'Unable to load the "Arcanist" library. Put "arcanist/" next to '.
|
||||
'"phabricator/" on disk.');
|
||||
}
|
||||
|
||||
phutil_load_library('arcanist/src');
|
||||
|
||||
// Load Phabricator itself using the absolute path, so we never end up doing
|
||||
// anything surprising (loading index.php and libraries from different
|
||||
// directories).
|
||||
|
|
Loading…
Reference in a new issue