diff --git a/CHANGELOG.md b/CHANGELOG.md index 2454b1111..8d336fc13 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,6 +17,9 @@ All notable changes to this project will be documented in this file. From versio - Add config `server-reuseport` to allow starting multiple PostgREST instances using the same port on supported platforms by @mkleczek in #4703, #4694 - Make config `log-level` reloadable by @taimoorzaeem in #5113 - Optimize schema cache domain type resolution by using `pg_basetype` on PostgreSQL 17+ by @joelonsql in #4567 +- `Prefer: timezone` is optimized so it no longer requires the schema cache by @steve-chavez in #5100 + + Previously this required caching `pg_timezone_names` which was slow in some systems +- `Prefer: timezone` now supports numeric offsets like `05:00` or `-4` by @steve-chavez in #5100 ### Fixed @@ -39,6 +42,7 @@ All notable changes to this project will be documented in this file. From versio - Build a static executable for aarch64-linux by @wolfgangwalther in #4193 - Build the minimal docker image for aarch64-linux by @wolfgangwalther in #4193 - Config `jwt-role-claim-key` now uses RFC 9535 syntax for JSON Path by @taimoorzaeem in #4984 +- `Prefer: timezone` no longer complies with `handling=lenient` and instead always fails by @steve-chavez in #5128 #### Changed Syntax for JWT Role Extraction diff --git a/docs/references/admin_server.rst b/docs/references/admin_server.rst index 65e19c519..316f35af0 100644 --- a/docs/references/admin_server.rst +++ b/docs/references/admin_server.rst @@ -74,5 +74,4 @@ Provides the ``schema_cache`` endpoint that prints the runtime :ref:`schema_cach "dbRepresentations": ["..."], "dbRoutines": ["..."], "dbTables": ["..."], - "dbTimezones": ["..."] } diff --git a/docs/references/api/preferences.rst b/docs/references/api/preferences.rst index 96dd4f48f..767b982f6 100644 --- a/docs/references/api/preferences.rst +++ b/docs/references/api/preferences.rst @@ -62,8 +62,11 @@ The server ignores unrecognized or unfulfillable preferences by default. You can Timezone ======== -The ``timezone`` preference allows you to change the `PostgreSQL timezone `_. It accepts all time zones in `pg_timezone_names `_. +.. important:: + ``handling=lenient`` is ignored for ``timezone``. Invalid time zones always return an error. + +The ``timezone`` preference allows you to change the `PostgreSQL timezone `_. It accepts all time zones in `pg_timezone_names `_. .. code-block:: bash @@ -84,35 +87,13 @@ The ``timezone`` preference allows you to change the `PostgreSQL timezone `). +For an invalid time zone, PostgREST returns a database error. .. code-block:: bash curl -i "http://localhost:3000/timestamps" \ -H "Prefer: timezone=Jupiter/Red_Spot" -.. code-block:: http - - HTTP/1.1 200 OK - Content-Type: application/json; charset=utf-8 - -.. code-block:: json - - [ - {"t":"2023-10-18T12:37:59.611+00:00"}, - {"t":"2023-10-18T14:37:59.611+00:00"}, - {"t":"2023-10-18T16:37:59.611+00:00"} - ] - -Note that there's no ``Preference-Applied`` in the response. - -However, with ``handling=strict``, an invalid time zone preference will throw an :ref:`error `. - -.. code-block:: bash - - curl -i "http://localhost:3000/timestamps" \ - -H "Prefer: handling=strict, timezone=Jupiter/Red_Spot" - .. code-block:: http HTTP/1.1 400 Bad Request diff --git a/src/library/PostgREST/ApiRequest.hs b/src/library/PostgREST/ApiRequest.hs index eb09e6e6e..d4cb875e3 100644 --- a/src/library/PostgREST/ApiRequest.hs +++ b/src/library/PostgREST/ApiRequest.hs @@ -33,7 +33,6 @@ import PostgREST.ApiRequest.Types (Action (..), DbAction (..), Payload (..), RequestBody, Resource (..)) import PostgREST.Config (AppConfig (..), OpenAPIMode (..)) -import PostgREST.Config.Database (TimezoneNames) import PostgREST.Error (ApiRequestError (..), RangeError (..)) import PostgREST.MediaType (MediaType (..)) import PostgREST.RangeQuery (NonnegRange, allRange, @@ -109,8 +108,8 @@ userApiRequest conf prefs req reqBody = do actIsInvokeSafe x = case x of {ActDb (ActRoutine _ (InvRead _)) -> True; _ -> False} -- | Parses the Prefer header -userPreferences :: AppConfig -> Request -> TimezoneNames -> Preferences.Preferences -userPreferences conf req timezones = Preferences.fromHeaders (configDbTxAllowOverride conf) timezones $ requestHeaders req +userPreferences :: AppConfig -> Request -> Preferences.Preferences +userPreferences conf req = Preferences.fromHeaders (configDbTxAllowOverride conf) $ requestHeaders req -- | Obtains the Bearer Auth userBearerAuth :: Request -> Maybe ByteString diff --git a/src/library/PostgREST/ApiRequest/Preferences.hs b/src/library/PostgREST/ApiRequest/Preferences.hs index 572474ffb..19f156455 100644 --- a/src/library/PostgREST/ApiRequest/Preferences.hs +++ b/src/library/PostgREST/ApiRequest/Preferences.hs @@ -26,11 +26,8 @@ module PostgREST.ApiRequest.Preferences import qualified Data.ByteString.Char8 as BS import qualified Data.Map as Map -import qualified Data.Set as S import qualified Network.HTTP.Types.Header as HTTP -import PostgREST.Config.Database (TimezoneNames) - import Protolude -- $setup @@ -66,10 +63,8 @@ data Preferences -- | -- Parse HTTP headers based on RFC7240[1] to identify preferences. -- --- >>> let sc = S.fromList ["America/Los_Angeles"] --- -- One header with comma-separated values can be used to set multiple preferences: --- >>> pPrint $ fromHeaders True sc [("Prefer", "resolution=ignore-duplicates, count=exact, timezone=America/Los_Angeles, max-affected=100")] +-- >>> pPrint $ fromHeaders True [("Prefer", "resolution=ignore-duplicates, count=exact, timezone=America/Los_Angeles, max-affected=100")] -- Preferences -- { preferResolution = Just IgnoreDuplicates -- , preferRepresentation = Nothing @@ -86,7 +81,7 @@ data Preferences -- -- Multiple headers can also be used: -- --- >>> pPrint $ fromHeaders True sc [("Prefer", "resolution=ignore-duplicates"), ("Prefer", "count=exact"), ("Prefer", "missing=null"), ("Prefer", "handling=lenient"), ("Prefer", "invalid"), ("Prefer", "max-affected=5999")] +-- >>> pPrint $ fromHeaders True [("Prefer", "resolution=ignore-duplicates"), ("Prefer", "count=exact"), ("Prefer", "missing=null"), ("Prefer", "handling=lenient"), ("Prefer", "invalid"), ("Prefer", "max-affected=5999")] -- Preferences -- { preferResolution = Just IgnoreDuplicates -- , preferRepresentation = Nothing @@ -102,13 +97,13 @@ data Preferences -- -- If a preference is set more than once, only the first is used: -- --- >>> preferTransaction $ fromHeaders True sc [("Prefer", "tx=commit, tx=rollback")] +-- >>> preferTransaction $ fromHeaders True [("Prefer", "tx=commit, tx=rollback")] -- Just Commit -- -- This is also the case across multiple headers: -- -- >>> :{ --- preferResolution . fromHeaders True sc $ +-- preferResolution . fromHeaders True $ -- [ ("Prefer", "resolution=ignore-duplicates") -- , ("Prefer", "resolution=merge-duplicates") -- ] @@ -118,7 +113,7 @@ data Preferences -- -- Preferences can be separated by arbitrary amounts of space, lower-case header is also recognized: -- --- >>> pPrint $ fromHeaders True sc [("prefer", "count=exact, tx=commit ,return=representation , missing=default, handling=strict, anything")] +-- >>> pPrint $ fromHeaders True [("prefer", "count=exact, tx=commit ,return=representation , missing=default, handling=strict, anything")] -- Preferences -- { preferResolution = Nothing -- , preferRepresentation = Just Full @@ -131,8 +126,8 @@ data Preferences -- , invalidPrefs = [ "anything" ] -- } -- -fromHeaders :: Bool -> TimezoneNames -> [HTTP.Header] -> Preferences -fromHeaders allowTxDbOverride acceptedTzNames headers = +fromHeaders :: Bool -> [HTTP.Header] -> Preferences +fromHeaders allowTxDbOverride headers = Preferences { preferResolution = parsePrefs [MergeDuplicates, IgnoreDuplicates] , preferRepresentation = parsePrefs [Full, None, HeadersOnly] @@ -140,7 +135,7 @@ fromHeaders allowTxDbOverride acceptedTzNames headers = , preferTransaction = if allowTxDbOverride then parsePrefs [Commit, Rollback] else Nothing , preferMissing = parsePrefs [ApplyDefaults, ApplyNulls] , preferHandling = parsePrefs [Strict, Lenient] - , preferTimezone = if isTimezonePrefAccepted then PreferTimezone <$> timezonePref else Nothing + , preferTimezone = PreferTimezone <$> timezonePref , preferMaxAffected = PreferMaxAffected <$> maxAffectedPref , invalidPrefs = filter isUnacceptable prefs } @@ -160,12 +155,11 @@ fromHeaders allowTxDbOverride acceptedTzNames headers = listStripPrefix prefix prefList = listToMaybe $ mapMaybe (BS.stripPrefix prefix) prefList timezonePref = listStripPrefix "timezone=" prefs - isTimezonePrefAccepted = ((S.member . decodeUtf8 <$> timezonePref) <*> pure acceptedTzNames) == Just True maxAffectedPref = listStripPrefix "max-affected=" prefs >>= readMaybe . BS.unpack isUnacceptable p = p `notElem` acceptedPrefs && - (isNothing (BS.stripPrefix "timezone=" p) || not isTimezonePrefAccepted) && + isNothing (BS.stripPrefix "timezone=" p) && isNothing (BS.stripPrefix "max-affected=" p) parsePrefs :: ToHeaderValue a => [a] -> Maybe a diff --git a/src/library/PostgREST/App.hs b/src/library/PostgREST/App.hs index dc8029e4d..0e18e9267 100644 --- a/src/library/PostgREST/App.hs +++ b/src/library/PostgREST/App.hs @@ -204,7 +204,7 @@ postgrestResponse appState conf@AppConfig{..} maybeSchemaCache jwtTime authResul liftIO $ observer SchemaCacheEmptyObs throwError Error.NoSchemaCacheError - let prefs = ApiRequest.userPreferences conf req (dbTimezones sCache) + let prefs = ApiRequest.userPreferences conf req body <- liftIO $ Wai.strictRequestBody req diff --git a/src/library/PostgREST/Config/Database.hs b/src/library/PostgREST/Config/Database.hs index 7283f7a02..d7e390975 100644 --- a/src/library/PostgREST/Config/Database.hs +++ b/src/library/PostgREST/Config/Database.hs @@ -7,7 +7,6 @@ module PostgREST.Config.Database , queryRoleSettings , RoleSettings , RoleIsolationLvl - , TimezoneNames , toIsolationLevel ) where @@ -31,7 +30,6 @@ import Protolude type RoleSettings = (HM.HashMap ByteString (HM.HashMap ByteString ByteString)) type RoleIsolationLvl = HM.HashMap ByteString SQL.IsolationLevel -type TimezoneNames = Set Text -- cache timezone names for prefer timezone= toIsolationLevel :: Text -> SQL.IsolationLevel toIsolationLevel a = case T.toLower a of diff --git a/src/library/PostgREST/SchemaCache.hs b/src/library/PostgREST/SchemaCache.hs index 1a7c5c78d..fe00fa322 100644 --- a/src/library/PostgREST/SchemaCache.hs +++ b/src/library/PostgREST/SchemaCache.hs @@ -45,7 +45,7 @@ import Data.Functor.Contravariant ((>$<)) import NeatInterpolation (trimming) import PostgREST.Config (AppConfig (..), LogLevel (..)) -import PostgREST.Config.Database (TimezoneNames, toIsolationLevel) +import PostgREST.Config.Database (toIsolationLevel) import PostgREST.Config.PgVersion (PgVersion, pgVersion170) import PostgREST.SchemaCache.Identifiers (FieldName, QualifiedIdentifier (..), @@ -78,7 +78,6 @@ data SchemaCache = SchemaCache , dbRoutines :: RoutineMap , dbRepresentations :: RepresentationsMap , dbMediaHandlers :: MediaHandlerMap - , dbTimezones :: TimezoneNames -- Memoized fuzzy index of table names per schema to support approximate matching -- Since index construction can be expensive, we build it once and store in the SchemaCache -- Haskell lazy evaluation ensures it's only built on first use and memoized afterwards @@ -86,24 +85,22 @@ data SchemaCache = SchemaCache } deriving (Show) instance JSON.ToJSON SchemaCache where - toJSON (SchemaCache tabs rels routs reps hdlers tzs _) = JSON.object [ + toJSON (SchemaCache tabs rels routs reps hdlers _) = JSON.object [ "dbTables" .= JSON.toJSON tabs , "dbRelationships" .= JSON.toJSON rels , "dbRoutines" .= JSON.toJSON routs , "dbRepresentations" .= JSON.toJSON reps , "dbMediaHandlers" .= JSON.toJSON hdlers - , "dbTimezones" .= JSON.toJSON tzs ] showSummary :: SchemaCache -> Text -showSummary (SchemaCache tbls rels routs reps mediaHdlrs tzs _) = +showSummary (SchemaCache tbls rels routs reps mediaHdlrs _) = T.intercalate ", " [ show (HM.size tbls) <> " Relations" , show (HM.size rels) <> " Relationships" , show (HM.size routs) <> " RPCs" , show (HM.size reps) <> " Domain Representations" , show (HM.size mediaHdlrs) <> " Media Type Handlers" - , show (S.size tzs) <> " Timezones" ] -- | A view foreign key or primary key dependency detected on its source table @@ -160,13 +157,12 @@ querySchemaCache pgVer conf@AppConfig{..} = do cRels <- sqlTimedStmt gucCRels mempty allComputedRels reps <- sqlTimedStmt gucDReps conf dataRepresentations mHdlers <- sqlTimedStmt gucMHdrs conf mediaHandlers - tzones <- sqlTimedStmt gucTzones mempty timezones for_ configInternalSCQuerySleepSnd (`SQL.statement` sleepCall) -- only used for testing qsTime <- if isLogDebug - then Just <$> SQL.statement mempty (extractTimings True) + then Just <$> SQL.statement mempty extractTimings else pure Nothing let tabsWViewsPks = addViewPrimaryKeys tabs keyDeps @@ -178,7 +174,6 @@ querySchemaCache pgVer conf@AppConfig{..} = do , dbRoutines = funcs , dbRepresentations = reps , dbMediaHandlers = HM.union mHdlers initialMediaHandlers -- the custom handlers will override the initial ones - , dbTimezones = tzones , dbTablesFuzzyIndex = -- Only build fuzzy index for schemas with a reasonable number of tables @@ -221,7 +216,6 @@ removeInternal schemas dbStruct = , dbRoutines = dbRoutines dbStruct -- procs are only obtained from the exposed schemas, no need to filter them. , dbRepresentations = dbRepresentations dbStruct -- no need to filter, not directly exposed through the API , dbMediaHandlers = dbMediaHandlers dbStruct - , dbTimezones = dbTimezones dbStruct , dbTablesFuzzyIndex = dbTablesFuzzyIndex dbStruct } where @@ -1108,19 +1102,6 @@ decodeMediaHandlers = <*> (MediaType.decodeMediaType . encodeUtf8 <$> column HD.text) <*> (MediaType.decodeMediaType . encodeUtf8 <$> column HD.text) -timezones :: SQL.Statement () TimezoneNames -timezones = SQL.Statement sql HE.noParams decodeTimezones True - where - sql = encodeUtf8 $ unlines - -- This CTE wrapper is only added for clarifying the query under pg_stat_statements - ["WITH pgrst_timezones AS (" - , " SELECT name FROM pg_timezone_names" - , ")" - , "SELECT * FROM pgrst_timezones" - ] - decodeTimezones :: HD.Result TimezoneNames - decodeTimezones = S.fromList <$> HD.rowList (column HD.text) - param :: HE.Value a -> HE.Params a param = HE.param . HE.nonNullable @@ -1168,21 +1149,21 @@ sqlTimedStatement isLogDebug guc params stmt = eFrag = "select set_config('pgrst." <> guc <> "', (clock_timestamp() - current_setting('pgrst." <> guc <> "', false)::timestamptz)::text, true)" -- Extract all the generated timings (see sqlTimedStatement) converting the value to milliseconds. -extractTimings :: Bool -> SQL.Statement () QueryTimings -extractTimings hasTimezones = SQL.Statement sql HE.noParams decodeThem True +extractTimings :: SQL.Statement () QueryTimings +extractTimings = SQL.Statement sql HE.noParams decodeThem True where qFrag setting = "extract('milliseconds' from current_setting('pgrst." <> setting <> "', false)::interval)::text" sql = "SELECT " <> BS.intercalate "," [ qFrag gucTbls, qFrag gucKDeps, qFrag gucRels , qFrag gucFuncs, qFrag gucCRels, qFrag gucDReps - , qFrag gucMHdrs, if hasTimezones then qFrag gucTzones else "'0.0'" + , qFrag gucMHdrs ] decodeThem :: HD.Result QueryTimings decodeThem = HD.singleRow $ QueryTimings <$> column HD.text <*> column HD.text <*> column HD.text <*> column HD.text <*> column HD.text <*> column HD.text - <*> column HD.text <*> column HD.text + <*> column HD.text data QueryTimings = QueryTimings { qtTables :: Text @@ -1192,7 +1173,6 @@ data QueryTimings = QueryTimings , qtCRels :: Text , qtDReps :: Text , qtMHdrs :: Text - , qtTzones :: Text } deriving (Show) queryTimingsWLabels :: QueryTimings -> [(ByteString, Text)] @@ -1204,10 +1184,9 @@ queryTimingsWLabels qt = , (gucCRels, qtCRels qt) , (gucDReps, qtDReps qt) , (gucMHdrs, qtMHdrs qt) - , (gucTzones, qtTzones qt) ] -gucTbls, gucKDeps, gucRels, gucFuncs, gucCRels, gucDReps, gucMHdrs, gucTzones :: ByteString +gucTbls, gucKDeps, gucRels, gucFuncs, gucCRels, gucDReps, gucMHdrs :: ByteString gucTbls = "tables" gucKDeps = "keydeps" gucRels = "rels" @@ -1215,4 +1194,3 @@ gucFuncs = "funcs" gucCRels = "comprels" gucDReps = "dreps" gucMHdrs = "mhandlers" -gucTzones = "tzones" diff --git a/test/io/__snapshots__/test_cli/test_schema_cache_snapshot[dbTimezones].yaml b/test/io/__snapshots__/test_cli/test_schema_cache_snapshot[dbTimezones].yaml deleted file mode 100644 index cbbe4e393..000000000 --- a/test/io/__snapshots__/test_cli/test_schema_cache_snapshot[dbTimezones].yaml +++ /dev/null @@ -1,1196 +0,0 @@ -- Africa/Abidjan -- Africa/Accra -- Africa/Addis_Ababa -- Africa/Algiers -- Africa/Asmara -- Africa/Asmera -- Africa/Bamako -- Africa/Bangui -- Africa/Banjul -- Africa/Bissau -- Africa/Blantyre -- Africa/Brazzaville -- Africa/Bujumbura -- Africa/Cairo -- Africa/Casablanca -- Africa/Ceuta -- Africa/Conakry -- Africa/Dakar -- Africa/Dar_es_Salaam -- Africa/Djibouti -- Africa/Douala -- Africa/El_Aaiun -- Africa/Freetown -- Africa/Gaborone -- Africa/Harare -- Africa/Johannesburg -- Africa/Juba -- Africa/Kampala -- Africa/Khartoum -- Africa/Kigali -- Africa/Kinshasa -- Africa/Lagos -- Africa/Libreville -- Africa/Lome -- Africa/Luanda -- Africa/Lubumbashi -- Africa/Lusaka -- Africa/Malabo -- Africa/Maputo -- Africa/Maseru -- Africa/Mbabane -- Africa/Mogadishu -- Africa/Monrovia -- Africa/Nairobi -- Africa/Ndjamena -- Africa/Niamey -- Africa/Nouakchott -- Africa/Ouagadougou -- Africa/Porto-Novo -- Africa/Sao_Tome -- Africa/Timbuktu -- Africa/Tripoli -- Africa/Tunis -- Africa/Windhoek -- America/Adak -- America/Anchorage -- America/Anguilla -- America/Antigua -- America/Araguaina -- America/Argentina/Buenos_Aires -- America/Argentina/Catamarca -- America/Argentina/ComodRivadavia -- America/Argentina/Cordoba -- America/Argentina/Jujuy -- America/Argentina/La_Rioja -- America/Argentina/Mendoza -- America/Argentina/Rio_Gallegos -- America/Argentina/Salta -- America/Argentina/San_Juan -- America/Argentina/San_Luis -- America/Argentina/Tucuman -- America/Argentina/Ushuaia -- America/Aruba -- America/Asuncion -- America/Atikokan -- America/Atka -- America/Bahia -- America/Bahia_Banderas -- America/Barbados -- America/Belem -- America/Belize -- America/Blanc-Sablon -- America/Boa_Vista -- America/Bogota -- America/Boise -- America/Buenos_Aires -- America/Cambridge_Bay -- America/Campo_Grande -- America/Cancun -- America/Caracas -- America/Catamarca -- America/Cayenne -- America/Cayman -- America/Chicago -- America/Chihuahua -- America/Ciudad_Juarez -- America/Coral_Harbour -- America/Cordoba -- America/Costa_Rica -- America/Coyhaique -- America/Creston -- America/Cuiaba -- America/Curacao -- America/Danmarkshavn -- America/Dawson -- America/Dawson_Creek -- America/Denver -- America/Detroit -- America/Dominica -- America/Edmonton -- America/Eirunepe -- America/El_Salvador -- America/Ensenada -- America/Fort_Nelson -- America/Fort_Wayne -- America/Fortaleza -- America/Glace_Bay -- America/Godthab -- America/Goose_Bay -- America/Grand_Turk -- America/Grenada -- America/Guadeloupe -- America/Guatemala -- America/Guayaquil -- America/Guyana -- America/Halifax -- America/Havana -- America/Hermosillo -- America/Indiana/Indianapolis -- America/Indiana/Knox -- America/Indiana/Marengo -- America/Indiana/Petersburg -- America/Indiana/Tell_City -- America/Indiana/Vevay -- America/Indiana/Vincennes -- America/Indiana/Winamac -- America/Indianapolis -- America/Inuvik -- America/Iqaluit -- America/Jamaica -- America/Jujuy -- America/Juneau -- America/Kentucky/Louisville -- America/Kentucky/Monticello -- America/Knox_IN -- America/Kralendijk -- America/La_Paz -- America/Lima -- America/Los_Angeles -- America/Louisville -- America/Lower_Princes -- America/Maceio -- America/Managua -- America/Manaus -- America/Marigot -- America/Martinique -- America/Matamoros -- America/Mazatlan -- America/Mendoza -- America/Menominee -- America/Merida -- America/Metlakatla -- America/Mexico_City -- America/Miquelon -- America/Moncton -- America/Monterrey -- America/Montevideo -- America/Montreal -- America/Montserrat -- America/Nassau -- America/New_York -- America/Nipigon -- America/Nome -- America/Noronha -- America/North_Dakota/Beulah -- America/North_Dakota/Center -- America/North_Dakota/New_Salem -- America/Nuuk -- America/Ojinaga -- America/Panama -- America/Pangnirtung -- America/Paramaribo -- America/Phoenix -- America/Port-au-Prince -- America/Port_of_Spain -- America/Porto_Acre -- America/Porto_Velho -- America/Puerto_Rico -- America/Punta_Arenas -- America/Rainy_River -- America/Rankin_Inlet -- America/Recife -- America/Regina -- America/Resolute -- America/Rio_Branco -- America/Rosario -- America/Santa_Isabel -- America/Santarem -- America/Santiago -- America/Santo_Domingo -- America/Sao_Paulo -- America/Scoresbysund -- America/Shiprock -- America/Sitka -- America/St_Barthelemy -- America/St_Johns -- America/St_Kitts -- America/St_Lucia -- America/St_Thomas -- America/St_Vincent -- America/Swift_Current -- America/Tegucigalpa -- America/Thule -- America/Thunder_Bay -- America/Tijuana -- America/Toronto -- America/Tortola -- America/Vancouver -- America/Virgin -- America/Whitehorse -- America/Winnipeg -- America/Yakutat -- America/Yellowknife -- Antarctica/Casey -- Antarctica/Davis -- Antarctica/DumontDUrville -- Antarctica/Macquarie -- Antarctica/Mawson -- Antarctica/McMurdo -- Antarctica/Palmer -- Antarctica/Rothera -- Antarctica/South_Pole -- Antarctica/Syowa -- Antarctica/Troll -- Antarctica/Vostok -- Arctic/Longyearbyen -- Asia/Aden -- Asia/Almaty -- Asia/Amman -- Asia/Anadyr -- Asia/Aqtau -- Asia/Aqtobe -- Asia/Ashgabat -- Asia/Ashkhabad -- Asia/Atyrau -- Asia/Baghdad -- Asia/Bahrain -- Asia/Baku -- Asia/Bangkok -- Asia/Barnaul -- Asia/Beirut -- Asia/Bishkek -- Asia/Brunei -- Asia/Calcutta -- Asia/Chita -- Asia/Choibalsan -- Asia/Chongqing -- Asia/Chungking -- Asia/Colombo -- Asia/Dacca -- Asia/Damascus -- Asia/Dhaka -- Asia/Dili -- Asia/Dubai -- Asia/Dushanbe -- Asia/Famagusta -- Asia/Gaza -- Asia/Harbin -- Asia/Hebron -- Asia/Ho_Chi_Minh -- Asia/Hong_Kong -- Asia/Hovd -- Asia/Irkutsk -- Asia/Istanbul -- Asia/Jakarta -- Asia/Jayapura -- Asia/Jerusalem -- Asia/Kabul -- Asia/Kamchatka -- Asia/Karachi -- Asia/Kashgar -- Asia/Kathmandu -- Asia/Katmandu -- Asia/Khandyga -- Asia/Kolkata -- Asia/Krasnoyarsk -- Asia/Kuala_Lumpur -- Asia/Kuching -- Asia/Kuwait -- Asia/Macao -- Asia/Macau -- Asia/Magadan -- Asia/Makassar -- Asia/Manila -- Asia/Muscat -- Asia/Nicosia -- Asia/Novokuznetsk -- Asia/Novosibirsk -- Asia/Omsk -- Asia/Oral -- Asia/Phnom_Penh -- Asia/Pontianak -- Asia/Pyongyang -- Asia/Qatar -- Asia/Qostanay -- Asia/Qyzylorda -- Asia/Rangoon -- Asia/Riyadh -- Asia/Saigon -- Asia/Sakhalin -- Asia/Samarkand -- Asia/Seoul -- Asia/Shanghai -- Asia/Singapore -- Asia/Srednekolymsk -- Asia/Taipei -- Asia/Tashkent -- Asia/Tbilisi -- Asia/Tehran -- Asia/Tel_Aviv -- Asia/Thimbu -- Asia/Thimphu -- Asia/Tokyo -- Asia/Tomsk -- Asia/Ujung_Pandang -- Asia/Ulaanbaatar -- Asia/Ulan_Bator -- Asia/Urumqi -- Asia/Ust-Nera -- Asia/Vientiane -- Asia/Vladivostok -- Asia/Yakutsk -- Asia/Yangon -- Asia/Yekaterinburg -- Asia/Yerevan -- Atlantic/Azores -- Atlantic/Bermuda -- Atlantic/Canary -- Atlantic/Cape_Verde -- Atlantic/Faeroe -- Atlantic/Faroe -- Atlantic/Jan_Mayen -- Atlantic/Madeira -- Atlantic/Reykjavik -- Atlantic/South_Georgia -- Atlantic/St_Helena -- Atlantic/Stanley -- Australia/ACT -- Australia/Adelaide -- Australia/Brisbane -- Australia/Broken_Hill -- Australia/Canberra -- Australia/Currie -- Australia/Darwin -- Australia/Eucla -- Australia/Hobart -- Australia/LHI -- Australia/Lindeman -- Australia/Lord_Howe -- Australia/Melbourne -- Australia/NSW -- Australia/North -- Australia/Perth -- Australia/Queensland -- Australia/South -- Australia/Sydney -- Australia/Tasmania -- Australia/Victoria -- Australia/West -- Australia/Yancowinna -- Brazil/Acre -- Brazil/DeNoronha -- Brazil/East -- Brazil/West -- CET -- CST6CDT -- Canada/Atlantic -- Canada/Central -- Canada/Eastern -- Canada/Mountain -- Canada/Newfoundland -- Canada/Pacific -- Canada/Saskatchewan -- Canada/Yukon -- Chile/Continental -- Chile/EasterIsland -- Cuba -- EET -- EST -- EST5EDT -- Egypt -- Eire -- Etc/GMT -- Etc/GMT+0 -- Etc/GMT+1 -- Etc/GMT+10 -- Etc/GMT+11 -- Etc/GMT+12 -- Etc/GMT+2 -- Etc/GMT+3 -- Etc/GMT+4 -- Etc/GMT+5 -- Etc/GMT+6 -- Etc/GMT+7 -- Etc/GMT+8 -- Etc/GMT+9 -- Etc/GMT-0 -- Etc/GMT-1 -- Etc/GMT-10 -- Etc/GMT-11 -- Etc/GMT-12 -- Etc/GMT-13 -- Etc/GMT-14 -- Etc/GMT-2 -- Etc/GMT-3 -- Etc/GMT-4 -- Etc/GMT-5 -- Etc/GMT-6 -- Etc/GMT-7 -- Etc/GMT-8 -- Etc/GMT-9 -- Etc/GMT0 -- Etc/Greenwich -- Etc/UCT -- Etc/UTC -- Etc/Universal -- Etc/Zulu -- Europe/Amsterdam -- Europe/Andorra -- Europe/Astrakhan -- Europe/Athens -- Europe/Belfast -- Europe/Belgrade -- Europe/Berlin -- Europe/Bratislava -- Europe/Brussels -- Europe/Bucharest -- Europe/Budapest -- Europe/Busingen -- Europe/Chisinau -- Europe/Copenhagen -- Europe/Dublin -- Europe/Gibraltar -- Europe/Guernsey -- Europe/Helsinki -- Europe/Isle_of_Man -- Europe/Istanbul -- Europe/Jersey -- Europe/Kaliningrad -- Europe/Kiev -- Europe/Kirov -- Europe/Kyiv -- Europe/Lisbon -- Europe/Ljubljana -- Europe/London -- Europe/Luxembourg -- Europe/Madrid -- Europe/Malta -- Europe/Mariehamn -- Europe/Minsk -- Europe/Monaco -- Europe/Moscow -- Europe/Nicosia -- Europe/Oslo -- Europe/Paris -- Europe/Podgorica -- Europe/Prague -- Europe/Riga -- Europe/Rome -- Europe/Samara -- Europe/San_Marino -- Europe/Sarajevo -- Europe/Saratov -- Europe/Simferopol -- Europe/Skopje -- Europe/Sofia -- Europe/Stockholm -- Europe/Tallinn -- Europe/Tirane -- Europe/Tiraspol -- Europe/Ulyanovsk -- Europe/Uzhgorod -- Europe/Vaduz -- Europe/Vatican -- Europe/Vienna -- Europe/Vilnius -- Europe/Volgograd -- Europe/Warsaw -- Europe/Zagreb -- Europe/Zaporozhye -- Europe/Zurich -- Factory -- GB -- GB-Eire -- GMT -- GMT+0 -- GMT-0 -- GMT0 -- Greenwich -- HST -- Hongkong -- Iceland -- Indian/Antananarivo -- Indian/Chagos -- Indian/Christmas -- Indian/Cocos -- Indian/Comoro -- Indian/Kerguelen -- Indian/Mahe -- Indian/Maldives -- Indian/Mauritius -- Indian/Mayotte -- Indian/Reunion -- Iran -- Israel -- Jamaica -- Japan -- Kwajalein -- Libya -- MET -- MST -- MST7MDT -- Mexico/BajaNorte -- Mexico/BajaSur -- Mexico/General -- NZ -- NZ-CHAT -- Navajo -- PRC -- PST8PDT -- Pacific/Apia -- Pacific/Auckland -- Pacific/Bougainville -- Pacific/Chatham -- Pacific/Chuuk -- Pacific/Easter -- Pacific/Efate -- Pacific/Enderbury -- Pacific/Fakaofo -- Pacific/Fiji -- Pacific/Funafuti -- Pacific/Galapagos -- Pacific/Gambier -- Pacific/Guadalcanal -- Pacific/Guam -- Pacific/Honolulu -- Pacific/Johnston -- Pacific/Kanton -- Pacific/Kiritimati -- Pacific/Kosrae -- Pacific/Kwajalein -- Pacific/Majuro -- Pacific/Marquesas -- Pacific/Midway -- Pacific/Nauru -- Pacific/Niue -- Pacific/Norfolk -- Pacific/Noumea -- Pacific/Pago_Pago -- Pacific/Palau -- Pacific/Pitcairn -- Pacific/Pohnpei -- Pacific/Ponape -- Pacific/Port_Moresby -- Pacific/Rarotonga -- Pacific/Saipan -- Pacific/Samoa -- Pacific/Tahiti -- Pacific/Tarawa -- Pacific/Tongatapu -- Pacific/Truk -- Pacific/Wake -- Pacific/Wallis -- Pacific/Yap -- Poland -- Portugal -- ROC -- ROK -- Singapore -- Turkey -- UCT -- US/Alaska -- US/Aleutian -- US/Arizona -- US/Central -- US/East-Indiana -- US/Eastern -- US/Hawaii -- US/Indiana-Starke -- US/Michigan -- US/Mountain -- US/Pacific -- US/Samoa -- UTC -- Universal -- W-SU -- WET -- Zulu -- posix/Africa/Abidjan -- posix/Africa/Accra -- posix/Africa/Addis_Ababa -- posix/Africa/Algiers -- posix/Africa/Asmara -- posix/Africa/Asmera -- posix/Africa/Bamako -- posix/Africa/Bangui -- posix/Africa/Banjul -- posix/Africa/Bissau -- posix/Africa/Blantyre -- posix/Africa/Brazzaville -- posix/Africa/Bujumbura -- posix/Africa/Cairo -- posix/Africa/Casablanca -- posix/Africa/Ceuta -- posix/Africa/Conakry -- posix/Africa/Dakar -- posix/Africa/Dar_es_Salaam -- posix/Africa/Djibouti -- posix/Africa/Douala -- posix/Africa/El_Aaiun -- posix/Africa/Freetown -- posix/Africa/Gaborone -- posix/Africa/Harare -- posix/Africa/Johannesburg -- posix/Africa/Juba -- posix/Africa/Kampala -- posix/Africa/Khartoum -- posix/Africa/Kigali -- posix/Africa/Kinshasa -- posix/Africa/Lagos -- posix/Africa/Libreville -- posix/Africa/Lome -- posix/Africa/Luanda -- posix/Africa/Lubumbashi -- posix/Africa/Lusaka -- posix/Africa/Malabo -- posix/Africa/Maputo -- posix/Africa/Maseru -- posix/Africa/Mbabane -- posix/Africa/Mogadishu -- posix/Africa/Monrovia -- posix/Africa/Nairobi -- posix/Africa/Ndjamena -- posix/Africa/Niamey -- posix/Africa/Nouakchott -- posix/Africa/Ouagadougou -- posix/Africa/Porto-Novo -- posix/Africa/Sao_Tome -- posix/Africa/Timbuktu -- posix/Africa/Tripoli -- posix/Africa/Tunis -- posix/Africa/Windhoek -- posix/America/Adak -- posix/America/Anchorage -- posix/America/Anguilla -- posix/America/Antigua -- posix/America/Araguaina -- posix/America/Argentina/Buenos_Aires -- posix/America/Argentina/Catamarca -- posix/America/Argentina/ComodRivadavia -- posix/America/Argentina/Cordoba -- posix/America/Argentina/Jujuy -- posix/America/Argentina/La_Rioja -- posix/America/Argentina/Mendoza -- posix/America/Argentina/Rio_Gallegos -- posix/America/Argentina/Salta -- posix/America/Argentina/San_Juan -- posix/America/Argentina/San_Luis -- posix/America/Argentina/Tucuman -- posix/America/Argentina/Ushuaia -- posix/America/Aruba -- posix/America/Asuncion -- posix/America/Atikokan -- posix/America/Atka -- posix/America/Bahia -- posix/America/Bahia_Banderas -- posix/America/Barbados -- posix/America/Belem -- posix/America/Belize -- posix/America/Blanc-Sablon -- posix/America/Boa_Vista -- posix/America/Bogota -- posix/America/Boise -- posix/America/Buenos_Aires -- posix/America/Cambridge_Bay -- posix/America/Campo_Grande -- posix/America/Cancun -- posix/America/Caracas -- posix/America/Catamarca -- posix/America/Cayenne -- posix/America/Cayman -- posix/America/Chicago -- posix/America/Chihuahua -- posix/America/Ciudad_Juarez -- posix/America/Coral_Harbour -- posix/America/Cordoba -- posix/America/Costa_Rica -- posix/America/Coyhaique -- posix/America/Creston -- posix/America/Cuiaba -- posix/America/Curacao -- posix/America/Danmarkshavn -- posix/America/Dawson -- posix/America/Dawson_Creek -- posix/America/Denver -- posix/America/Detroit -- posix/America/Dominica -- posix/America/Edmonton -- posix/America/Eirunepe -- posix/America/El_Salvador -- posix/America/Ensenada -- posix/America/Fort_Nelson -- posix/America/Fort_Wayne -- posix/America/Fortaleza -- posix/America/Glace_Bay -- posix/America/Godthab -- posix/America/Goose_Bay -- posix/America/Grand_Turk -- posix/America/Grenada -- posix/America/Guadeloupe -- posix/America/Guatemala -- posix/America/Guayaquil -- posix/America/Guyana -- posix/America/Halifax -- posix/America/Havana -- posix/America/Hermosillo -- posix/America/Indiana/Indianapolis -- posix/America/Indiana/Knox -- posix/America/Indiana/Marengo -- posix/America/Indiana/Petersburg -- posix/America/Indiana/Tell_City -- posix/America/Indiana/Vevay -- posix/America/Indiana/Vincennes -- posix/America/Indiana/Winamac -- posix/America/Indianapolis -- posix/America/Inuvik -- posix/America/Iqaluit -- posix/America/Jamaica -- posix/America/Jujuy -- posix/America/Juneau -- posix/America/Kentucky/Louisville -- posix/America/Kentucky/Monticello -- posix/America/Knox_IN -- posix/America/Kralendijk -- posix/America/La_Paz -- posix/America/Lima -- posix/America/Los_Angeles -- posix/America/Louisville -- posix/America/Lower_Princes -- posix/America/Maceio -- posix/America/Managua -- posix/America/Manaus -- posix/America/Marigot -- posix/America/Martinique -- posix/America/Matamoros -- posix/America/Mazatlan -- posix/America/Mendoza -- posix/America/Menominee -- posix/America/Merida -- posix/America/Metlakatla -- posix/America/Mexico_City -- posix/America/Miquelon -- posix/America/Moncton -- posix/America/Monterrey -- posix/America/Montevideo -- posix/America/Montreal -- posix/America/Montserrat -- posix/America/Nassau -- posix/America/New_York -- posix/America/Nipigon -- posix/America/Nome -- posix/America/Noronha -- posix/America/North_Dakota/Beulah -- posix/America/North_Dakota/Center -- posix/America/North_Dakota/New_Salem -- posix/America/Nuuk -- posix/America/Ojinaga -- posix/America/Panama -- posix/America/Pangnirtung -- posix/America/Paramaribo -- posix/America/Phoenix -- posix/America/Port-au-Prince -- posix/America/Port_of_Spain -- posix/America/Porto_Acre -- posix/America/Porto_Velho -- posix/America/Puerto_Rico -- posix/America/Punta_Arenas -- posix/America/Rainy_River -- posix/America/Rankin_Inlet -- posix/America/Recife -- posix/America/Regina -- posix/America/Resolute -- posix/America/Rio_Branco -- posix/America/Rosario -- posix/America/Santa_Isabel -- posix/America/Santarem -- posix/America/Santiago -- posix/America/Santo_Domingo -- posix/America/Sao_Paulo -- posix/America/Scoresbysund -- posix/America/Shiprock -- posix/America/Sitka -- posix/America/St_Barthelemy -- posix/America/St_Johns -- posix/America/St_Kitts -- posix/America/St_Lucia -- posix/America/St_Thomas -- posix/America/St_Vincent -- posix/America/Swift_Current -- posix/America/Tegucigalpa -- posix/America/Thule -- posix/America/Thunder_Bay -- posix/America/Tijuana -- posix/America/Toronto -- posix/America/Tortola -- posix/America/Vancouver -- posix/America/Virgin -- posix/America/Whitehorse -- posix/America/Winnipeg -- posix/America/Yakutat -- posix/America/Yellowknife -- posix/Antarctica/Casey -- posix/Antarctica/Davis -- posix/Antarctica/DumontDUrville -- posix/Antarctica/Macquarie -- posix/Antarctica/Mawson -- posix/Antarctica/McMurdo -- posix/Antarctica/Palmer -- posix/Antarctica/Rothera -- posix/Antarctica/South_Pole -- posix/Antarctica/Syowa -- posix/Antarctica/Troll -- posix/Antarctica/Vostok -- posix/Arctic/Longyearbyen -- posix/Asia/Aden -- posix/Asia/Almaty -- posix/Asia/Amman -- posix/Asia/Anadyr -- posix/Asia/Aqtau -- posix/Asia/Aqtobe -- posix/Asia/Ashgabat -- posix/Asia/Ashkhabad -- posix/Asia/Atyrau -- posix/Asia/Baghdad -- posix/Asia/Bahrain -- posix/Asia/Baku -- posix/Asia/Bangkok -- posix/Asia/Barnaul -- posix/Asia/Beirut -- posix/Asia/Bishkek -- posix/Asia/Brunei -- posix/Asia/Calcutta -- posix/Asia/Chita -- posix/Asia/Choibalsan -- posix/Asia/Chongqing -- posix/Asia/Chungking -- posix/Asia/Colombo -- posix/Asia/Dacca -- posix/Asia/Damascus -- posix/Asia/Dhaka -- posix/Asia/Dili -- posix/Asia/Dubai -- posix/Asia/Dushanbe -- posix/Asia/Famagusta -- posix/Asia/Gaza -- posix/Asia/Harbin -- posix/Asia/Hebron -- posix/Asia/Ho_Chi_Minh -- posix/Asia/Hong_Kong -- posix/Asia/Hovd -- posix/Asia/Irkutsk -- posix/Asia/Istanbul -- posix/Asia/Jakarta -- posix/Asia/Jayapura -- posix/Asia/Jerusalem -- posix/Asia/Kabul -- posix/Asia/Kamchatka -- posix/Asia/Karachi -- posix/Asia/Kashgar -- posix/Asia/Kathmandu -- posix/Asia/Katmandu -- posix/Asia/Khandyga -- posix/Asia/Kolkata -- posix/Asia/Krasnoyarsk -- posix/Asia/Kuala_Lumpur -- posix/Asia/Kuching -- posix/Asia/Kuwait -- posix/Asia/Macao -- posix/Asia/Macau -- posix/Asia/Magadan -- posix/Asia/Makassar -- posix/Asia/Manila -- posix/Asia/Muscat -- posix/Asia/Nicosia -- posix/Asia/Novokuznetsk -- posix/Asia/Novosibirsk -- posix/Asia/Omsk -- posix/Asia/Oral -- posix/Asia/Phnom_Penh -- posix/Asia/Pontianak -- posix/Asia/Pyongyang -- posix/Asia/Qatar -- posix/Asia/Qostanay -- posix/Asia/Qyzylorda -- posix/Asia/Rangoon -- posix/Asia/Riyadh -- posix/Asia/Saigon -- posix/Asia/Sakhalin -- posix/Asia/Samarkand -- posix/Asia/Seoul -- posix/Asia/Shanghai -- posix/Asia/Singapore -- posix/Asia/Srednekolymsk -- posix/Asia/Taipei -- posix/Asia/Tashkent -- posix/Asia/Tbilisi -- posix/Asia/Tehran -- posix/Asia/Tel_Aviv -- posix/Asia/Thimbu -- posix/Asia/Thimphu -- posix/Asia/Tokyo -- posix/Asia/Tomsk -- posix/Asia/Ujung_Pandang -- posix/Asia/Ulaanbaatar -- posix/Asia/Ulan_Bator -- posix/Asia/Urumqi -- posix/Asia/Ust-Nera -- posix/Asia/Vientiane -- posix/Asia/Vladivostok -- posix/Asia/Yakutsk -- posix/Asia/Yangon -- posix/Asia/Yekaterinburg -- posix/Asia/Yerevan -- posix/Atlantic/Azores -- posix/Atlantic/Bermuda -- posix/Atlantic/Canary -- posix/Atlantic/Cape_Verde -- posix/Atlantic/Faeroe -- posix/Atlantic/Faroe -- posix/Atlantic/Jan_Mayen -- posix/Atlantic/Madeira -- posix/Atlantic/Reykjavik -- posix/Atlantic/South_Georgia -- posix/Atlantic/St_Helena -- posix/Atlantic/Stanley -- posix/Australia/ACT -- posix/Australia/Adelaide -- posix/Australia/Brisbane -- posix/Australia/Broken_Hill -- posix/Australia/Canberra -- posix/Australia/Currie -- posix/Australia/Darwin -- posix/Australia/Eucla -- posix/Australia/Hobart -- posix/Australia/LHI -- posix/Australia/Lindeman -- posix/Australia/Lord_Howe -- posix/Australia/Melbourne -- posix/Australia/NSW -- posix/Australia/North -- posix/Australia/Perth -- posix/Australia/Queensland -- posix/Australia/South -- posix/Australia/Sydney -- posix/Australia/Tasmania -- posix/Australia/Victoria -- posix/Australia/West -- posix/Australia/Yancowinna -- posix/Brazil/Acre -- posix/Brazil/DeNoronha -- posix/Brazil/East -- posix/Brazil/West -- posix/CET -- posix/CST6CDT -- posix/Canada/Atlantic -- posix/Canada/Central -- posix/Canada/Eastern -- posix/Canada/Mountain -- posix/Canada/Newfoundland -- posix/Canada/Pacific -- posix/Canada/Saskatchewan -- posix/Canada/Yukon -- posix/Chile/Continental -- posix/Chile/EasterIsland -- posix/Cuba -- posix/EET -- posix/EST -- posix/EST5EDT -- posix/Egypt -- posix/Eire -- posix/Etc/GMT -- posix/Etc/GMT+0 -- posix/Etc/GMT+1 -- posix/Etc/GMT+10 -- posix/Etc/GMT+11 -- posix/Etc/GMT+12 -- posix/Etc/GMT+2 -- posix/Etc/GMT+3 -- posix/Etc/GMT+4 -- posix/Etc/GMT+5 -- posix/Etc/GMT+6 -- posix/Etc/GMT+7 -- posix/Etc/GMT+8 -- posix/Etc/GMT+9 -- posix/Etc/GMT-0 -- posix/Etc/GMT-1 -- posix/Etc/GMT-10 -- posix/Etc/GMT-11 -- posix/Etc/GMT-12 -- posix/Etc/GMT-13 -- posix/Etc/GMT-14 -- posix/Etc/GMT-2 -- posix/Etc/GMT-3 -- posix/Etc/GMT-4 -- posix/Etc/GMT-5 -- posix/Etc/GMT-6 -- posix/Etc/GMT-7 -- posix/Etc/GMT-8 -- posix/Etc/GMT-9 -- posix/Etc/GMT0 -- posix/Etc/Greenwich -- posix/Etc/UCT -- posix/Etc/UTC -- posix/Etc/Universal -- posix/Etc/Zulu -- posix/Europe/Amsterdam -- posix/Europe/Andorra -- posix/Europe/Astrakhan -- posix/Europe/Athens -- posix/Europe/Belfast -- posix/Europe/Belgrade -- posix/Europe/Berlin -- posix/Europe/Bratislava -- posix/Europe/Brussels -- posix/Europe/Bucharest -- posix/Europe/Budapest -- posix/Europe/Busingen -- posix/Europe/Chisinau -- posix/Europe/Copenhagen -- posix/Europe/Dublin -- posix/Europe/Gibraltar -- posix/Europe/Guernsey -- posix/Europe/Helsinki -- posix/Europe/Isle_of_Man -- posix/Europe/Istanbul -- posix/Europe/Jersey -- posix/Europe/Kaliningrad -- posix/Europe/Kiev -- posix/Europe/Kirov -- posix/Europe/Kyiv -- posix/Europe/Lisbon -- posix/Europe/Ljubljana -- posix/Europe/London -- posix/Europe/Luxembourg -- posix/Europe/Madrid -- posix/Europe/Malta -- posix/Europe/Mariehamn -- posix/Europe/Minsk -- posix/Europe/Monaco -- posix/Europe/Moscow -- posix/Europe/Nicosia -- posix/Europe/Oslo -- posix/Europe/Paris -- posix/Europe/Podgorica -- posix/Europe/Prague -- posix/Europe/Riga -- posix/Europe/Rome -- posix/Europe/Samara -- posix/Europe/San_Marino -- posix/Europe/Sarajevo -- posix/Europe/Saratov -- posix/Europe/Simferopol -- posix/Europe/Skopje -- posix/Europe/Sofia -- posix/Europe/Stockholm -- posix/Europe/Tallinn -- posix/Europe/Tirane -- posix/Europe/Tiraspol -- posix/Europe/Ulyanovsk -- posix/Europe/Uzhgorod -- posix/Europe/Vaduz -- posix/Europe/Vatican -- posix/Europe/Vienna -- posix/Europe/Vilnius -- posix/Europe/Volgograd -- posix/Europe/Warsaw -- posix/Europe/Zagreb -- posix/Europe/Zaporozhye -- posix/Europe/Zurich -- posix/Factory -- posix/GB -- posix/GB-Eire -- posix/GMT -- posix/GMT+0 -- posix/GMT-0 -- posix/GMT0 -- posix/Greenwich -- posix/HST -- posix/Hongkong -- posix/Iceland -- posix/Indian/Antananarivo -- posix/Indian/Chagos -- posix/Indian/Christmas -- posix/Indian/Cocos -- posix/Indian/Comoro -- posix/Indian/Kerguelen -- posix/Indian/Mahe -- posix/Indian/Maldives -- posix/Indian/Mauritius -- posix/Indian/Mayotte -- posix/Indian/Reunion -- posix/Iran -- posix/Israel -- posix/Jamaica -- posix/Japan -- posix/Kwajalein -- posix/Libya -- posix/MET -- posix/MST -- posix/MST7MDT -- posix/Mexico/BajaNorte -- posix/Mexico/BajaSur -- posix/Mexico/General -- posix/NZ -- posix/NZ-CHAT -- posix/Navajo -- posix/PRC -- posix/PST8PDT -- posix/Pacific/Apia -- posix/Pacific/Auckland -- posix/Pacific/Bougainville -- posix/Pacific/Chatham -- posix/Pacific/Chuuk -- posix/Pacific/Easter -- posix/Pacific/Efate -- posix/Pacific/Enderbury -- posix/Pacific/Fakaofo -- posix/Pacific/Fiji -- posix/Pacific/Funafuti -- posix/Pacific/Galapagos -- posix/Pacific/Gambier -- posix/Pacific/Guadalcanal -- posix/Pacific/Guam -- posix/Pacific/Honolulu -- posix/Pacific/Johnston -- posix/Pacific/Kanton -- posix/Pacific/Kiritimati -- posix/Pacific/Kosrae -- posix/Pacific/Kwajalein -- posix/Pacific/Majuro -- posix/Pacific/Marquesas -- posix/Pacific/Midway -- posix/Pacific/Nauru -- posix/Pacific/Niue -- posix/Pacific/Norfolk -- posix/Pacific/Noumea -- posix/Pacific/Pago_Pago -- posix/Pacific/Palau -- posix/Pacific/Pitcairn -- posix/Pacific/Pohnpei -- posix/Pacific/Ponape -- posix/Pacific/Port_Moresby -- posix/Pacific/Rarotonga -- posix/Pacific/Saipan -- posix/Pacific/Samoa -- posix/Pacific/Tahiti -- posix/Pacific/Tarawa -- posix/Pacific/Tongatapu -- posix/Pacific/Truk -- posix/Pacific/Wake -- posix/Pacific/Wallis -- posix/Pacific/Yap -- posix/Poland -- posix/Portugal -- posix/ROC -- posix/ROK -- posix/Singapore -- posix/Turkey -- posix/UCT -- posix/US/Alaska -- posix/US/Aleutian -- posix/US/Arizona -- posix/US/Central -- posix/US/East-Indiana -- posix/US/Eastern -- posix/US/Hawaii -- posix/US/Indiana-Starke -- posix/US/Michigan -- posix/US/Mountain -- posix/US/Pacific -- posix/US/Samoa -- posix/UTC -- posix/Universal -- posix/W-SU -- posix/WET -- posix/Zulu diff --git a/test/io/test_cli.py b/test/io/test_cli.py index c378afcdf..d13f62b32 100644 --- a/test/io/test_cli.py +++ b/test/io/test_cli.py @@ -237,7 +237,6 @@ def test_invalid_openapi_mode(invalidopenapimodes, defaultenv): "dbRepresentations", "dbRoutines", "dbTables", - "dbTimezones", ], ) def test_schema_cache_snapshot(baseenv, key, snapshot_yaml): @@ -248,7 +247,7 @@ def test_schema_cache_snapshot(baseenv, key, snapshot_yaml): schema_cache[key], encoding="utf8", allow_unicode=True, - Dumper=yaml.SafeDumper if key == "dbTimezones" else ExtraNewLinesDumper, + Dumper=ExtraNewLinesDumper, ) assert formatted == snapshot_yaml diff --git a/test/io/test_io.py b/test/io/test_io.py index 763613906..33cc1346a 100644 --- a/test/io/test_io.py +++ b/test/io/test_io.py @@ -1002,9 +1002,8 @@ def test_schema_cache_query_timings_log(level, defaultenv): **defaultenv, "PGRST_LOG_LEVEL": level, } - # here we also capture the tzones: ms log_pattern = re.compile( - r".+: tables: [\d.]+ ms, keydeps: [\d.]+ ms, rels: [\d.]+ ms, funcs: [\d.]+ ms, comprels: [\d.]+ ms, dreps: [\d.]+ ms, mhandlers: [\d.]+ ms, tzones: ([\d.]+) ms" + r".+: tables: [\d.]+ ms, keydeps: [\d.]+ ms, rels: [\d.]+ ms, funcs: [\d.]+ ms, comprels: [\d.]+ ms, dreps: [\d.]+ ms, mhandlers: [\d.]+ ms" ) with run(env=env, no_startup_stdout=False) as postgrest: @@ -1015,7 +1014,6 @@ def test_schema_cache_query_timings_log(level, defaultenv): if level == "debug": assert len(timing_matches) == 1 - assert float(timing_matches[0].group(1)) > 0 else: assert not timing_matches diff --git a/test/spec/Feature/Query/Preferences/TimezoneSpec.hs b/test/spec/Feature/Query/Preferences/TimezoneSpec.hs index e52a359db..fae2431dd 100644 --- a/test/spec/Feature/Query/Preferences/TimezoneSpec.hs +++ b/test/spec/Feature/Query/Preferences/TimezoneSpec.hs @@ -9,52 +9,49 @@ import Protolude hiding (get) import SpecHelper spec :: SpecWithConfig -spec withConfig = withConfig baseCfg $ - describe "test Prefer: timezone with db-timezone-enabled is true" $ do - context "test Prefer: timezone=America/Los_Angeles" $ do - it "should change timezone with handling=strict" $ - request methodGet "/timestamps" - [("Prefer", "handling=strict, timezone=America/Los_Angeles")] - "" - `shouldRespondWith` - [json|[{"t":"2023-10-18T05:37:59.611-07:00"}, {"t":"2023-10-18T07:37:59.611-07:00"}, {"t":"2023-10-18T09:37:59.611-07:00"}]|] - { matchStatus = 200 - , matchHeaders = [matchContentTypeJson - , "Preference-Applied" <:> "handling=strict, timezone=America/Los_Angeles"]} +spec withConfig = withConfig baseCfg $ do + context "test Prefer: timezone=America/Los_Angeles" $ do + it "should change timezone with handling=strict" $ + request methodGet "/timestamps" + [("Prefer", "handling=strict, timezone=America/Los_Angeles")] + "" + `shouldRespondWith` + [json|[{"t":"2023-10-18T05:37:59.611-07:00"}, {"t":"2023-10-18T07:37:59.611-07:00"}, {"t":"2023-10-18T09:37:59.611-07:00"}]|] + { matchStatus = 200 + , matchHeaders = [matchContentTypeJson + , "Preference-Applied" <:> "handling=strict, timezone=America/Los_Angeles"]} - it "should change timezone without handling=strict" $ - request methodGet "/timestamps" - [("Prefer", "timezone=America/Los_Angeles")] - "" - `shouldRespondWith` - [json|[{"t":"2023-10-18T05:37:59.611-07:00"}, {"t":"2023-10-18T07:37:59.611-07:00"}, {"t":"2023-10-18T09:37:59.611-07:00"}]|] - { matchStatus = 200 - , matchHeaders = [matchContentTypeJson - , "Preference-Applied" <:> "timezone=America/Los_Angeles"] } + it "should change timezone without handling=strict" $ + request methodGet "/timestamps" + [("Prefer", "timezone=America/Los_Angeles")] + "" + `shouldRespondWith` + [json|[{"t":"2023-10-18T05:37:59.611-07:00"}, {"t":"2023-10-18T07:37:59.611-07:00"}, {"t":"2023-10-18T09:37:59.611-07:00"}]|] + { matchStatus = 200 + , matchHeaders = [matchContentTypeJson + , "Preference-Applied" <:> "timezone=America/Los_Angeles"] } - context "test Prefer: timezone=Invalid/Timezone" $ do - it "should throw error with handling=strict" $ - request methodGet "/timestamps" - [("Prefer", "handling=strict, timezone=Invalid/Timezone")] - "" - `shouldRespondWith` - [json|{"code":"PGRST122","details":"Invalid preferences: timezone=Invalid/Timezone","hint":null,"message":"Invalid preferences given with handling=strict"}|] - { matchStatus = 400 } + context "test Prefer: timezone=Invalid/Timezone" $ do + it "should throw error without handling" $ + request methodGet "/timestamps" + [("Prefer", "timezone=Invalid/Timezone")] + "" + `shouldRespondWith` + [json|{"code":"22023","details":null,"hint":null,"message":"invalid value for parameter \"TimeZone\": \"Invalid/Timezone\""}|] + { matchStatus = 400 } - it "should return with default timezone without handling or with handling=lenient" $ do - request methodGet "/timestamps" - [("Prefer", "timezone=Invalid/Timezone")] - "" - `shouldRespondWith` - [json|[{"t":"2023-10-18T12:37:59.611+00:00"}, {"t":"2023-10-18T14:37:59.611+00:00"}, {"t":"2023-10-18T16:37:59.611+00:00"}]|] - { matchStatus = 200 - , matchHeaders = [matchContentTypeJson]} + it "should throw error with handling=strict" $ + request methodGet "/timestamps" + [("Prefer", "handling=strict, timezone=Invalid/Timezone")] + "" + `shouldRespondWith` + [json|{"code":"22023","details":null,"hint":null,"message":"invalid value for parameter \"TimeZone\": \"Invalid/Timezone\""}|] + { matchStatus = 400 } - request methodGet "/timestamps" - [("Prefer", "handling=lenient, timezone=Invalid/Timezone")] - "" - `shouldRespondWith` - [json|[{"t":"2023-10-18T12:37:59.611+00:00"}, {"t":"2023-10-18T14:37:59.611+00:00"}, {"t":"2023-10-18T16:37:59.611+00:00"}]|] - { matchStatus = 200 - , matchHeaders = [matchContentTypeJson - , "Preference-Applied" <:> "handling=lenient"]} + it "should throw error with handling=lenient" $ + request methodGet "/timestamps" + [("Prefer", "handling=lenient, timezone=Invalid/Timezone")] + "" + `shouldRespondWith` + [json|{"code":"22023","details":null,"hint":null,"message":"invalid value for parameter \"TimeZone\": \"Invalid/Timezone\""}|] + { matchStatus = 400 }