Как справиться с переходом на летнее время с помощью TimeZone в Java
Я должен напечатать время EST в моем приложении Java. Я установил часовой пояс на EST, используя:
Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("EST"));
Но когда в этом часовом поясе соблюдается переход на летнее время, мой код не печатает правильное время (он печатает на 1 час меньше).
Как заставить код работать так, чтобы он всегда читал правильное время, независимо от того, наблюдается летнее время или нет?
PS: я пытался установить часовой пояс на EDT, но это не решает проблему.
8 ответов
Это проблема для начала:
Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("EST"));
Следует полностью избегать трехбуквенных сокращений в пользу идентификаторов зон TZDB. EST - восточное стандартное время, а стандартное время никогда не соблюдает DST; это не совсем полное название часового пояса. Это имя используется для части часового пояса. (К сожалению, я не нашел хорошего термина для этой концепции "половины часового пояса".)
Вы хотите полное название часового пояса. Например, America/New_York
находится в восточном часовом поясе:
TimeZone zone = TimeZone.getTimeZone("America/New_York");
DateFormat format = DateFormat.getDateTimeInstance();
format.setTimeZone(zone);
System.out.println(format.format(new Date()));
Другие ответы верны, особенно Джон Скит, но устарели.
java.time
Эти старые классы даты и времени были вытеснены фреймворком java.time, встроенным в Java 8 и более поздние версии.
Если вы просто хотите узнать текущее время в UTC, используйтеInstant
учебный класс.
Instant now = Instant.now();
EST
это не часовой пояс, как объясняется в правильном ответе Джона Скита. Такие 3-4-буквенные коды не являются ни стандартизированными, ни уникальными, что еще больше усложняет задачу перехода на летнее время (DST). Используйте правильное имя часового пояса в формате "континент / регион".
Возможно, вы имели в виду восточное стандартное время на восточном побережье Северной Америки? Или стандартное время Египта? Или европейское стандартное время?
ZoneId zoneId = ZoneId.of( "America/New_York" );
ZoneId zoneId = ZoneId.of( "Africa/Cairo" );
ZoneId zoneId = ZoneId.of( "Europe/Lisbon" );
Используйте любой такойZoneId
объект, чтобы получить текущий момент, настроенный на определенный часовой пояс, чтобы произвестиZonedDateTime
объект.
ZonedDateTime zdt = ZonedDateTime.now( zoneId ) ;
Настройте этот ZonedDateTime в другой часовой пояс, создав первый объект ZonedDateTime из первого. Инфраструктура java.time использует неизменяемые объекты, а не изменяет (мутирует) существующие объекты.
ZonedDateTime zdtGuam = zdt.withZoneSameInstant( ZoneId.of( "Pacific/Guam" ) ) ;
Вместо ввода "EST" для часового пояса вы можете ввести "EST5EDT" как таковой. Как вы заметили, просто "EDT" не работает. Это будет учитывать проблему перехода на летнее время. Строка кода выглядит следующим образом:
Calendar cal = Calendar.getInstance(TimeZone.getTimeZone("EST5EDT"));
private static Long DateTimeNowTicks(){
long TICKS_AT_EPOCH = 621355968000000000L;
TimeZone timeZone = Calendar.getInstance().getTimeZone();
int offs = timeZone.getRawOffset();
if (timeZone.inDaylightTime(new Date()))
offs += 60 * 60 * 1000;
return (System.currentTimeMillis() + offs) * 10000 + TICKS_AT_EPOCH;
}
Согласно этому ответу:
TimeZone tz = TimeZone.getTimeZone("EST");
boolean inDs = tz.inDaylightTime(new Date());
public static float calculateTimeZone(String deviceTimeZone) {
float ONE_HOUR_MILLIS = 60 * 60 * 1000;
// Current timezone and date
TimeZone timeZone = TimeZone.getTimeZone(deviceTimeZone);
Date nowDate = new Date();
float offsetFromUtc = timeZone.getOffset(nowDate.getTime()) / ONE_HOUR_MILLIS;
// Daylight Saving time
if (timeZone.useDaylightTime()) {
// DST is used
// I'm saving this is preferences for later use
// save the offset value to use it later
float dstOffset = timeZone.getDSTSavings() / ONE_HOUR_MILLIS;
// DstOffsetValue = dstOffset
// I'm saving this is preferences for later use
// save that now we are in DST mode
if (timeZone.inDaylightTime(nowDate)) {
Log.e(Utility.class.getName(), "in Daylight Time");
return -(ONE_HOUR_MILLIS * dstOffset);
} else {
Log.e(Utility.class.getName(), "not in Daylight Time");
return 0;
}
} else
return 0;
}
В java DateFormatter по умолчанию использует DST, чтобы избежать перехода на летнее время (DST), вам нужно сделать трюк вручную,
сначала вы должны получить смещение DST, т. е. на сколько миллисекунд применено DST, для ex где-то DST также составляет 45 минут, а для некоторых мест - 30 минут.
но в большинстве случаев летнее время составляет 1 час
Вы должны использовать объект Timezone и проверить с датой, подпадает ли он под DST или нет, а затем вы должны вручную добавить смещение DST в него. например:
TimeZone tz = TimeZone.getTimeZone("EST");
boolean isDST = tz.inDaylightTime(yourDateObj);
if(isDST){
int sec= tz.getDSTSavings()/1000;// for no. of seconds
Calendar cal= Calendar.getInstance();
cal.setTime(yourDateObj);
cal.add(Calendar.Seconds,sec);
System.out.println(cal.getTime());// your Date with DST neglected
}
Реализация класса TimeZone для установки часового пояса в календарь заботится о переходе на летнее время.
java.util.TimeZone представляет смещение часового пояса, а также вычисляет летнее время.
образец кода:
TimeZone est_timeZone = TimeZoneIDProvider.getTimeZoneID(TimeZoneID.US_EASTERN).getTimeZone();
Calendar enteredCalendar = Calendar.getInstance();
enteredCalendar.setTimeZone(est_timeZone);