Index: wp-includes/default-filters.php
===================================================================
--- wp-includes/default-filters.php	(revision 39414)
+++ wp-includes/default-filters.php	(working copy)
@@ -385,6 +385,7 @@
 add_action( 'rest_api_init', 'register_initial_settings',  10 );
 add_action( 'rest_api_init', 'create_initial_rest_routes', 99 );
 add_action( 'parse_request', 'rest_api_loaded' );
+add_filter( 'rest_pre_update_setting', 'rest_pre_update_settings', 10, 4 );
 
 /**
  * Filters formerly mixed into wp-includes
Index: wp-includes/option.php
===================================================================
--- wp-includes/option.php	(revision 39414)
+++ wp-includes/option.php	(working copy)
@@ -1771,6 +1771,12 @@
 		'description'  => __( 'A city in the same timezone as you.' ),
 	) );
 
+	register_setting( 'general', 'gmt_offset', array(
+		'show_in_rest' => true,
+		'type'         => 'string',
+		'description'  => __( 'UTC timezone offset.' ),
+	) );
+
 	register_setting( 'general', 'date_format', array(
 		'show_in_rest' => true,
 		'type'         => 'string',
@@ -2010,3 +2016,24 @@
 
 	return $registered[ $option ]['default'];
 }
+
+/**
+ * Runs before the value of an option is updated via the REST API.
+ *
+ * @param bool   $result Whether to override the default behavior for updating the
+ *                       value of a setting.
+ * @param string $name   Setting name (as shown in REST API responses).
+ * @param mixed  $value  Updated setting value.
+ * @param array  $args   Arguments passed to register_setting() for this setting.
+ * @return bool False if value was not updated and true if value was updated.
+ */
+function rest_pre_update_settings( $result, $name, $value, $args ) {
+	switch ( $name ) {
+		case 'gmt_offset':
+			update_option( 'timezone_string', '' );
+			update_option( 'gmt_offset', $value );
+			return true;
+		default:
+			return $result;
+	}
+}
