มีวิธีใดในการแปลง ZoneId เป็น ZoneOffset ใน java 8 หรือไม่?


89

ฉันมี epoch วินาทีและ zoneId โดย method1 มันสามารถแปลงเป็น LocalDateTime ด้วย zoneId เริ่มต้นของระบบได้ แต่ฉันไม่พบวิธีแปลง epoch วินาทีเป็น LocalDateTime โดย method2 เพราะไม่มีZoneOffset.systemDefaultฉันคิดว่ามันคลุมเครือ

import java.time.{Instant, LocalDateTime, ZoneId, ZoneOffset}

val epochSecond = System.currentTimeMillis() / 1000

LocalDateTime.ofInstant(Instant.ofEpochSecond(epochSecond), ZoneId.systemDefault())//method1
LocalDateTime.ofEpochSecond(epochSecond, 0, ZoneOffset.MAX)//method2

คุณมีอะไรภาษา / ภาษาที่ใช้จะทำให้การนำเข้ากลุ่มimport java.time.{Instant, LocalDateTime, ZoneId, ZoneOffset}ทำงานหรือไม่ มันเป็นunexpected tokenสำหรับฉันในชวา 11
anddero

1
อาจเป็น Scala แต่นั่นไม่สำคัญจริงๆที่นี่
donatas M

1
ฉันได้พูดถึงทฤษฎีที่อยู่เบื้องหลังjava.timeความยาวแล้วที่นี่: stackoverflow.com/a/56508200/145989
Ondra Žižka

คำตอบ:


113

นี่คือวิธีที่คุณจะได้รับZoneOffsetจากZoneId:

Instant instant = Instant.now(); //can be LocalDateTime
ZoneId systemZone = ZoneId.systemDefault(); // my timezone
ZoneOffset currentOffsetForMyZone = systemZone.getRules().getOffset(instant);

หมายเหตุ: ZoneIdอาจมีค่าชดเชยที่แตกต่างกันขึ้นอยู่กับช่วงเวลาและประวัติของสถานที่นั้น ๆ ดังนั้นการเลือกอินสแตนซ์ที่แตกต่างกันจะส่งผลให้ออฟเซ็ตต่างกัน

NB2: ZoneId.of()สามารถกลับมาเป็นZoneOffsetแทนZoneIdถ้าUTC+3/ GMT+2/ etc Africa/Cairoถูกส่งผ่านไปเมื่อเทียบกับโซนเวลาเช่น ดังนั้นหากมีการผ่านการชดเชย UTC / GMT ข้อมูลในอดีต / ภูมิศาสตร์ / การประหยัดเวลากลางวันของสิ่งเหล่าInstantนี้จะไม่ถูกนำมาพิจารณา - คุณจะดำเนินการกับค่าชดเชยที่ระบุ


สิ่งนี้ให้ค่าที่ไม่ถูกต้องหากทันทีปัจจุบันอยู่ใน DST แต่ในขณะคำนวณนั้นไม่มี DST
msangel

@msangel คุณช่วยตรวจสอบอีกครั้งได้ไหม บางทีคุณกำลังใช้ZoneId.systemDefault()รหัสของคุณและส่งกลับโซนเวลาที่แตกต่างกันในเครื่องต่างๆ
Stanislav Bashkyrtsev

ฉันตรวจสอบแล้วและใช้งานได้ขึ้นอยู่กับเงื่อนไขอื่น อัปเดตคำตอบด้วยสิ่งนั้น
msangel

43

ไม่มีการทำแผนที่แบบหนึ่งต่อหนึ่ง ZoneId กำหนดขอบเขตทางภูมิศาสตร์ที่ใช้ชุดของ ZoneOffsets ที่แตกต่างกันในช่วงเวลาหนึ่ง หากเขตเวลาใช้เวลาออมแสง ZoneOffset จะแตกต่างกันระหว่างฤดูร้อนและฤดูหนาว

นอกจากนี้กฎเวลาออมแสงอาจมีการเปลี่ยนแปลงเมื่อเวลาผ่านไปดังนั้น ZoneOffset จึงอาจแตกต่างกันเช่น 13/10/2015 เมื่อเทียบกับ 13/10/1980

ดังนั้นคุณจะพบ ZoneOffset สำหรับ ZoneId ใน Instant ที่ต้องการเท่านั้น

ดูhttps://en.wikipedia.org/wiki/Tz_database


10
ฉันรู้ แต่มันทำอย่างไรถ้าฉันรู้ LocaDate และมี ZoneId ฉันจะแปลงเป็น ZoneOffset ได้อย่างไร กรณีการใช้งานของฉันคือสตริงที่มีเวลาและโซน ฉันต้องการแยกวิเคราะห์สตริงเป็น OffsetTime โดยไม่ต้องดร็อปข้อมูลโซน
Kai Moritz

35

tl; dr

ZonedDateTime.now( 
    ZoneId.of( "America/Montreal" ) 
)

…ของเขตเวลาเริ่มต้นปัจจุบัน…

ZonedDateTime.now( 
    ZoneId.systemDefault() 
)

รายละเอียด

คำตอบโดยสตานิส Bshkyrtsevอย่างถูกต้องและตรงตอบคำถามของคุณ

แต่มีปัญหาขนาดใหญ่ที่เกี่ยวข้องกับการตามข้อเสนอแนะในคำตอบโดยจอนสกีต

LocalDateTime

ฉันไม่พบวิธีการแปลงช่วงเวลาที่สองเป็น LocalDateTime

LocalDateTimeโดยเจตนาไม่มีแนวคิดเรื่องเขตเวลาหรือ offset-from-UTC ไม่น่าจะเป็นสิ่งที่คุณต้องการ Local…หมายใด ๆท้องที่ไม่คนใดคนหนึ่งท้องที่ใดท้องที่หนึ่ง ชั้นนี้ไม่ได้หมายถึงช่วงเวลาเท่านั้นที่อาจเกิดขึ้นในช่วงเวลาที่พร้อมช่วงประมาณ 26-27 ชั่วโมง (ช่วงของโซนเวลาทั่วโลกบริการ)

Instant

ไม่จำเป็นต้องเริ่มต้นด้วยช่วงวินาทีหากคุณพยายามหาเวลาปัจจุบัน Instantได้รับในปัจจุบัน Instantชั้นหมายถึงช่วงเวลาบนไทม์ไลน์ในส่วนUTCมีความละเอียดของนาโนวินาที (ถึงเก้า (9) ตัวเลขของส่วนทศนิยม)

Instant instant = Instant.now();

ข้างในนั้นInstantคือจำนวนนับนาโนวินาที - จากยุค แต่เราไม่สนใจจริงๆ

ZonedDateTime

หากคุณต้องการเห็นช่วงเวลานั้นผ่านเลนส์นาฬิกาแขวนของภูมิภาคใดภูมิภาคหนึ่งให้ใช้ a ZoneIdให้ใช้ a ZonedDateTime.

ZoneId z = ZoneId.of( "Europe/Paris" );
ZonedDateTime zdt = instant.atZone( z );

เป็นทางลัดคุณสามารถทำได้โดยตรงกับไฟล์ ZonedDateTime .

ZonedDateTime zdt = ZonedDateTime.now( z );  

A ZonedDateTimeมีInstantอยู่ภายใน โทรzdt.toInstant()เพื่อรับช่วงเวลาเดียวกันเป็นค่าพื้นฐานใน UTC จำนวนนาโนวินาทีตั้งแต่ยุคเท่ากันทั้งแบบ a ZonedDateTimeหรือแบบInstant.

วินาทีตั้งแต่ยุคที่กำหนด

หากคุณได้รับการนับจำนวนวินาทีตั้งแต่ยุคและยุคเป็นช่วงเวลาแรกของปี 1970 ในUTC ( 1970-01-01T00:00:00Z) ให้ป้อนตัวเลขInstantนั้นเป็น

long secondsSinceEpoch = 1_484_063_246L ;
Instant instant = Instant.ofEpochSecond( secondsSinceEpoch ) ;

ตารางประเภทวันที่ - เวลาใน Java ทั้งแบบสมัยใหม่และแบบเดิม


เกี่ยวกับ java.time

java.timeกรอบถูกสร้างขึ้นใน Java 8 และต่อมา ชั้นเรียนเหล่านี้แย่งลำบากเก่ามรดกเรียนวันที่เวลาเช่นjava.util.Date,CalendarSimpleDateFormatและ

ต้องการเรียนรู้เพิ่มเติมโปรดดูที่ออราเคิลกวดวิชา และค้นหา Stack Overflow สำหรับตัวอย่างและคำอธิบายมากมาย ข้อมูลจำเพาะคือ JSR 310

โครงการJoda-Timeขณะนี้อยู่ในโหมดการบำรุงรักษาแนะนำให้ย้ายไปยังคลาสjava.time

คุณสามารถแลกเปลี่ยนวัตถุjava.timeโดยตรงกับฐานข้อมูลของคุณ ใช้ไดรเวอร์ JDBC ที่สอดคล้องกับJDBC 4.2หรือใหม่กว่า ไม่จำเป็นต้องมีสตริงไม่จำเป็นต้องมีjava.sql.*คลาส

จะหาคลาส java.time ได้ที่ไหน?

  • Java SE 8 , Java SE 9 , Java SE 10 , Java SE 11และใหม่กว่า - เป็นส่วนหนึ่งของ Java API มาตรฐานพร้อมการใช้งานแบบรวม
    • Java 9 เพิ่มคุณสมบัติและการแก้ไขเล็กน้อย
  • Java SE 6และ Java SE 7
    • ส่วนใหญ่ของjava.timeการทำงานจะกลับรังเพลิง Java 6 และ 7 ในThreeTen-ย้ายกลับ
  • Android

โครงการThreeTen-Extraขยาย java.time ด้วยคลาสเพิ่มเติม โปรเจ็กต์นี้เป็นพื้นที่พิสูจน์สำหรับการเพิ่ม java.time ในอนาคต คุณอาจพบว่าการเรียนที่มีประโยชน์บางอย่างที่นี่เช่นInterval, YearWeek, YearQuarterและอื่น ๆ อีกมากมาย


10

ตามที่เอกสารระบุว่า "โดยหลักแล้วมีไว้สำหรับ Conversion ระดับต่ำมากกว่าการใช้งานแอปพลิเคชันทั่วไป"

การก้าวผ่านไปมีInstantความหมายที่ดีสำหรับฉัน - ช่วงเวลาที่สองของคุณเป็นการแสดงที่แตกต่างกันอย่างมีประสิทธิภาพInstantดังนั้นจงแปลงเป็นInstantและแปลงเป็นเขตเวลาเฉพาะ


9

ฉันหวังว่าสองบรรทัดแรกของวิธีแก้ปัญหาด้านล่างจะเป็นประโยชน์ ปัญหาของฉันคือฉันมีLocalDateTimeและชื่อเขตเวลาและฉันต้องการinstantเพื่อที่ฉันจะได้สร้าง a java.util.Dateเพราะนั่นคือสิ่งที่ MongoDB ต้องการ รหัสของฉันคือ Scala แต่ใกล้กับ Java มากที่นี่ฉันคิดว่าไม่น่าจะมีปัญหาในการทำความเข้าใจ:

val zid = ZoneId.of(tzName)                                // "America/Los_Angeles"
val zo: ZoneOffset = zid.getRules.getOffset(localDateTime) // ⇒ -07:00
                                         // 2017-03-16T18:03

val odt = OffsetDateTime.of(localDateTime, zo) // ⇒ 2017-03-16T18:03:00-07:00
val instant = odt.toInstant                    // ⇒ 2017-03-17T01:03:00Z
val issued = Date.from(instant)

1
ฉันโพสต์คำตอบนี้ที่นี่เพราะนี่คือหน้าเว็บที่เกิดขึ้นเมื่อฉันค้นหาวิธีทำในที่สุดฉันก็คิดออก
gknauth

2

ต่อไปนี้จะคืนค่าระยะเวลาเป็นมิลลิวินาทีเพื่อเพิ่มลงใน UTC เพื่อรับเวลามาตรฐานในเขตเวลานี้:

TimeZone.getTimeZone(ZoneId.of("Europe/Amsterdam")).getRawOffset()

0

ฉันมียุคที่สองและ zoneId มีวิธีใดในการแปลง ZoneId เป็น ZoneOffset ใน java 8 หรือไม่?

  1. รับ ZonedDateTimeจาก epoch second และ Zone Id
  2. รับZoneOffsetจากZonedDateTime

การสาธิต:

import java.time.Instant;
import java.time.ZoneId;
import java.time.ZoneOffset;
import java.time.ZonedDateTime;

public class Main {
    public static void main(String[] args) {
        // Get ZonedDateTime from epoch second and Zone Id
        ZonedDateTime zdt = Instant.ofEpochSecond(1597615462L).atZone(ZoneId.of("Europe/London"));

        // Get ZoneOffset from ZonedDateTime
        ZoneOffset offset = zdt.getOffset();

        System.out.println(offset);
    }
}

เอาท์พุต:

+01:00
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.