Поиск идентификатора зоны по текущему времени в этой зоне

#java #java-time

Вопрос:

Каков наилучший способ найти идентификатор зоны по ожидаемым часам в этом идентификаторе зоны по времени UTC?

Например,

Текущее время в UTC:
2021-10-22T11:40:37.808Z

Необходимо найти любой идентификатор зоны, где текущие часы в идентификаторе зоны будут:
17

17-11 = 6
Итак, мы ищем любой идентификатор зоны, который имеет:
UTC 06:00

Например, это:
Азия / Кашгар (UTC 06:00)

Комментарии:

1. Это странное требование. Вы уверены, что это правильный способ решить проблему, которую вы на самом деле пытаетесь решить? Что вы на самом деле пытаетесь решить, делая это?

2. Это необходимо для тестов e2e. У каждого пользователя свой часовой пояс. Мне нужно проверить, что если пользователь входит в систему в 7 утра по своему часовому поясу, api возвращает «доступ запрещен». Для этого мне нужно взять текущее время в UTC, найти и установить часовой пояс пользователя, где будет 7 утра по текущему времени в UTC.

3. Если это для тестирования, как насчет простого использования ZoneOffset.ofHours(6) ? Вам действительно нужно имя часового пояса для чего-нибудь?

4. Мне нужно имя часового пояса, потому что в базе данных часовой пояс пользователя хранится как имя часового пояса.

5. Почему вы просто не вызываете ZoneId.systemDefault() , если хотите узнать текущий часовой пояс пользователя по умолчанию?

Ответ №1:

Вы можете получить все ZoneId s и соответствующие смещения.

 import java.time.*;
import java.util.*;

class Main {  
  public static void main(String args[]) { 
    System.out.println(zoneIdToOffsetMap()); 
  }

private static Map<String, String> zoneIdToOffsetMap() {
        Map<String, String> result = new HashMap<>();

        LocalDateTime localDateTime = LocalDateTime.now();

        for (String zoneId : ZoneId.getAvailableZoneIds()) {
            ZoneId id = ZoneId.of(zoneId);
            // LocalDateTime -> ZonedDateTime
            ZonedDateTime zonedDateTime = localDateTime.atZone(id);
            // ZonedDateTime -> ZoneOffset
            ZoneOffset zoneOffset = zonedDateTime.getOffset();
            //replace Z to  00:00
            String offset = zoneOffset.getId().replaceAll("Z", " 00:00");

            result.put(id.toString(), offset);
        }

        return resu<
    }
}
 

При этом выводится карта, подобная:

 {Asia/Aden= 03:00, America/Cuiaba=-04:00, ...}
 

Если вам действительно нужны все значения ZoneId s для заданного смещения, вы можете немного изменить приведенный выше код, чтобы вернуть карту String -> List<String> или просто a List<String> для одного смещения, как показано ниже.

 private static List<String> getZoneIds(String target) {
        List<String> zoneIds = new ArrayList<>();

        LocalDateTime localDateTime = LocalDateTime.now();

        for (String zoneId : ZoneId.getAvailableZoneIds()) {
            ZoneId id = ZoneId.of(zoneId);
            // LocalDateTime -> ZonedDateTime
            ZonedDateTime zonedDateTime = localDateTime.atZone(id);
            // ZonedDateTime -> ZoneOffset
            ZoneOffset zoneOffset = zonedDateTime.getOffset();
            //replace Z to  00:00
            String offset = zoneOffset.getId().replaceAll("Z", " 00:00");

            if (offset.equals(target)) {
                zoneIds.add(id.toString());
            }
        }

        return zoneIds;
    }
}
 

Это выводит:

 [Asia/Kashgar, Etc/GMT-6, Asia/Almaty, Asia/Dacca, Asia/Omsk, Asia/Dhaka, Indian/Chagos, Asia/Qostanay, Asia/Bishkek, Antarctica/Vostok, Asia/Urumqi, Asia/Thimbu, Asia/Thimphu]
 

Ответ №2:

Вы сказали:

Итак, мы ищем любой идентификатор зоны, который имеет: UTC 06:00

Смещение от UTC, используемое часовым поясом, меняется со временем. Это само определение часового пояса: история прошлых, настоящих и будущих изменений смещения, используемого жителями определенного региона по решению их политиков.

Поэтому мы должны указать момент, an Instant , для которого мы хотим знать текущее действующее смещение.

В приведенном ниже коде мы используем поток для рассмотрения каждого из всех часовых поясов. Для каждого часового пояса мы извлекаем его набор правил, чтобы история изменений смещалась. Мы запрашиваем эти правила для смещения, используемого в выбранный нами момент. Для возвращаемого смещения мы сравниваем, чтобы увидеть, является ли это смещением, которое мы хотим.

 Instant instant = Instant.now() ;
ZoneOffset targetOffset = ZoneOffset.ofHours( 6 ) ;
List< ZoneId > zonesUsingDesiredOffset = 
    ZoneId
    .getAvailableZoneIds()  // Returns a `Set` of `String` objects, the names of each known time zone.
    .stream()
    .map( zoneName -> ZoneId.of( zoneName ) )  // Map each zone name to `ZoneId` object.
    .filter(
        zoneId -> zoneId.getRules().getOffset( instant ).equals( targetOffset )  // Filter for zones whose offset at specified moment matches our desired offset.
    )
    .collect( Collectors.toList() )
;
 

Смотрите, как этот код выполняется в режиме реального времени по адресу IdeOne.com .

[Азия / Кашгар и т.д. / GMT-6, Азия / Алматы, Азия / Дакка, Азия / Омск, Азия / Дакка, Индия / Чагос, Азия / Кызылорда, Азия / Бишкек, Антарктида / Восток, Азия / Урумчи, Азия / Тхимбу, Азия / Тхимпху]