ฉันแยกวิเคราะห์java.util.Date
จาก a แล้วString
แต่เป็นการตั้งค่าเขตเวลาท้องถิ่นเป็นเขตเวลาของdate
วัตถุ
โซนเวลาที่ไม่ได้ระบุไว้ในString
จากการที่Date
จะแยก ฉันต้องการตั้งค่าเขตเวลาเฉพาะของdate
วัตถุ
ฉันจะทำสิ่งนั้นได้อย่างไร
ฉันแยกวิเคราะห์java.util.Date
จาก a แล้วString
แต่เป็นการตั้งค่าเขตเวลาท้องถิ่นเป็นเขตเวลาของdate
วัตถุ
โซนเวลาที่ไม่ได้ระบุไว้ในString
จากการที่Date
จะแยก ฉันต้องการตั้งค่าเขตเวลาเฉพาะของdate
วัตถุ
ฉันจะทำสิ่งนั้นได้อย่างไร
คำตอบ:
ใช้ DateFormat ตัวอย่างเช่น,
SimpleDateFormat isoFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
isoFormat.setTimeZone(TimeZone.getTimeZone("UTC"));
Date date = isoFormat.parse("2010-05-23T09:01:02");
TimeZone.setDefault()
โทรก่อนgetTime()
เพื่อที่วัตถุวันที่ใหม่จะอยู่ในเขตเวลาที่คุณต้องการ ใน JDK 1.8 เรียกCalendar.getTime()
return new Date(getTimeInMillis());
โปรดระวังว่าjava.util.Date
วัตถุไม่มีข้อมูลเขตเวลาด้วยตนเอง - คุณไม่สามารถตั้งค่าเขตเวลาบนDate
วัตถุได้ สิ่งเดียวที่Date
วัตถุมีจำนวนมิลลิวินาทีนับตั้งแต่ "ยุค" - 1 มกราคม 1970, 00:00:00 UTC
เมื่อ ZZ Coder แสดงขึ้นคุณตั้งเขตเวลาบนDateFormat
วัตถุเพื่อบอกเขตเวลาที่คุณต้องการแสดงวันที่และเวลา
long
ใช้จริงเพื่อตีความเวลามิลลิวินาทีนี้ ดังนั้นการพิมพ์ a ทำให้ดูเหมือนจะมีเขตเวลา (ค่าเริ่มต้น) นำไปสู่คำถามที่เข้าใจได้เกี่ยวกับวิธีตั้งค่าเขตเวลานั้น fastTime
Date.toString()
Calendar
Date
…ไม่ได้ระบุ…แยกวิเคราะห์…จาก String …เขตเวลา…ฉันต้องการตั้งค่าเขตเวลาเฉพาะ
LocalDateTime.parse( "2018-01-23T01:23:45.123456789" ) // Parse string, lacking an offset-from-UTC and lacking a time zone, as a `LocalDateTime`.
.atZone( ZoneId.of( "Africa/Tunis" ) ) // Assign the time zone for which you are certain this date-time was intended. Instantiates a `ZonedDateTime` object.
ในฐานะที่เป็นคำตอบที่ถูกต้องอื่น ๆ ที่ระบุไว้เป็น java.util.Date มีโซนเวลา† เพราะมันหมายถึงUTC / GMT (ไม่มีการชดเชยโซนเวลา) สับสนมากเพราะtoString
วิธีนี้ใช้โซนเวลาเริ่มต้นของ JVM เมื่อสร้างการแทนค่าสตริง
ด้วยเหตุผลนี้และอีกหลายเหตุผลคุณควรหลีกเลี่ยงการใช้ java.util.Date & .Calendar ในตัว & java.text.SimpleDateFormat พวกเขามีปัญหาอย่างฉาวโฉ่
ใช้แพ็คเกจ java.time ที่มาพร้อมกับJava 8แทน
คลาส java.time สามารถแสดงช่วงเวลาบนไทม์ไลน์ได้สามวิธี:
Instant
)OffsetDateTime
พร้อมZoneOffset
)ZonedDateTime
พร้อมZoneId
)Instant
ในjava.time Building Blockพื้นฐานคือInstant
หนึ่งช่วงเวลาบนเส้นเวลาใน UTC ใช้Instant
วัตถุสำหรับตรรกะทางธุรกิจส่วนใหญ่ของคุณ
Instant instant = Instant.now();
OffsetDateTime
ใช้การชดเชยจาก UTCที่จะปรับลงในท้องที่บางเวลาผนังนาฬิกา
สมัครที่จะได้รับZoneOffset
OffsetDateTime
ZoneOffset zoneOffset = ZoneOffset.of( "-04:00" );
OffsetDateTime odt = OffsetDateTime.ofInstant( instant , zoneOffset );
ZonedDateTime
ที่ดีคือการใช้โซนเวลาการชดเชยบวกกฎสำหรับการจัดการความผิดปกติเช่นปรับเวลาตามฤดูกาล (DST)
ใช้ZoneId
กับผู้ที่จะได้รับInstant
ZonedDateTime
ระบุชื่อโซนเวลาที่ถูกต้องเสมอ ห้ามใช้ตัวย่อ 3-4 ตัวเช่นEST
หรือIST
ที่ไม่ซ้ำกันหรือไม่เป็นมาตรฐาน
ZoneId zoneId = ZoneId.of( "America/Montreal" );
ZonedDateTime zdt = ZonedDateTime.ofInstant( instant , zoneId );
LocalDateTime
LocalDateTime
หากสายป้อนขาดบ่งชี้ของการชดเชยหรือโซนแยกเป็นใด ๆ
หากคุณเป็นหนึ่งของโซนเวลาที่ตั้งใจไว้กำหนดในการผลิตZoneId
ZonedDateTime
ดูตัวอย่างโค้ดด้านบนในส่วนtl; drที่ด้านบน
เรียกใช้toString
เมธอดของคลาสทั้งสามนี้เพื่อสร้าง String ที่แทนค่าวันที่และเวลาในรูปแบบISO 8601 มาตรฐาน ZonedDateTime
ระดับขยายรูปแบบมาตรฐานโดยต่อท้ายชื่อของโซนเวลาในวงเล็บ
String outputInstant = instant.toString(); // Ex: 2011-12-03T10:15:30Z
String outputOdt = odt.toString(); // Ex: 2007-12-03T10:15:30+01:00
String outputZdt = zdt.toString(); // Ex: 2007-12-03T10:15:30+01:00[Europe/Paris]
สำหรับรูปแบบอื่นให้ใช้DateTimeFormatter
คลาสนี้ โดยทั่วไปแล้วดีที่สุดที่จะให้คลาสนั้นสร้างรูปแบบที่มีการแปลโดยใช้ภาษามนุษย์และบรรทัดฐานทางวัฒนธรรมที่คาดหวังของผู้ใช้ หรือคุณสามารถระบุรูปแบบเฉพาะ
java.timeกรอบถูกสร้างขึ้นใน Java 8 และต่อมา ชั้นเรียนเหล่านี้แย่งลำบากเก่ามรดกเรียนวันที่เวลาเช่นjava.util.Date
, และCalendar
SimpleDateFormat
Joda เวลาโครงการขณะนี้อยู่ในโหมดการบำรุงรักษาให้คำแนะนำแก่การโยกย้ายไปยังjava.timeชั้นเรียน
ต้องการเรียนรู้เพิ่มเติมโปรดดูที่ออราเคิลกวดวิชา และค้นหา Stack Overflow สำหรับตัวอย่างและคำอธิบายมากมาย สเปกJSR 310
คุณสามารถแลกเปลี่ยนวัตถุjava.timeโดยตรงกับฐานข้อมูลของคุณ ใช้ไดรเวอร์ JDBC ที่สอดคล้องกับJDBC 4.2หรือใหม่กว่า ไม่จำเป็นต้องใช้สตริงไม่จำเป็นต้องมีjava.sql.*
คลาส
จะรับคลาส java.time ได้ที่ไหน?
โครงการThreeTen-Extraขยาย java.time ด้วยคลาสเพิ่มเติม โครงการนี้เป็นพื้นฐานที่พิสูจน์ได้สำหรับการเพิ่มเติมในอนาคตไปยัง java.time คุณอาจพบว่าการเรียนที่มีประโยชน์บางอย่างที่นี่เช่นInterval
, YearWeek
, YearQuarter
และอื่น ๆ อีกมากมาย
ในขณะที่Joda-Timeยังคงได้รับการบำรุงรักษาอย่างแข็งขันผู้ผลิตบอกให้เราย้ายไปยัง java.time ทันทีที่สะดวก ฉันปล่อยให้ส่วนนี้เป็นข้อมูลอ้างอิง แต่ฉันแนะนำให้ใช้java.time
ส่วนด้านบนแทน
ในJoda-Timeวัตถุวันที่ ( DateTime
) จะทราบเขตเวลาที่กำหนดอย่างแท้จริง นั่นหมายถึงการชดเชยจาก UTC และกฎและประวัติของ Daylight Saving Time (DST) ของเขตเวลาและความผิดปกติอื่น ๆ
String input = "2014-01-02T03:04:05";
DateTimeZone timeZone = DateTimeZone.forID( "Asia/Kolkata" );
DateTime dateTimeIndia = new DateTime( input, timeZone );
DateTime dateTimeUtcGmt = dateTimeIndia.withZone( DateTimeZone.UTC );
เรียกtoString
วิธีการสร้าง String ในรูปแบบISO 8601
String output = dateTimeIndia.toString();
Joda-Time ยังมีความสามารถมากมายสำหรับการสร้างรูปแบบสตริงอื่น ๆ ทุกประเภท
หากจำเป็นคุณสามารถแปลงจาก Joda-Time DateTime เป็น java.util.Date
Java.util.Date date = dateTimeIndia.toDate();
ค้นหา StackOverflow สำหรับ "joda date" เพื่อหาตัวอย่างอื่น ๆ อีกมากมายบางรายละเอียดค่อนข้างมาก
†จริงมีเป็นโซนเวลาที่ฝังอยู่ใน java.util.Date ใช้สำหรับฟังก์ชั่นบางอย่างภายใน (ดูความคิดเห็นเกี่ยวกับคำตอบนี้) แต่เขตเวลาภายในนี้จะไม่ถูกเปิดเผยเป็นคุณสมบัติและไม่สามารถตั้งค่าได้ เขตเวลาภายในนี้ไม่ใช่โซนที่ใช้โดยtoString
วิธีการในการสร้างการแสดงสตริงของค่าวันที่และเวลา แทนเขตเวลาเริ่มต้นปัจจุบันของ JVM จะถูกนำไปใช้กับ on-the-fly แทน ดังนั้นตามที่จดชวเลขเรามักจะพูดว่า "juDate ไม่มีโซนเวลา" ทำให้เกิดความสับสน? ใช่. ยังเป็นอีกเหตุผลหนึ่งที่จะหลีกเลี่ยงคลาสเก่าเหล่านี้ที่เหนื่อยล้า
BaseCalendar.Date cdate
คุณสมบัติหากตั้งไว้ ลองดูที่รหัสแหล่งที่มาที่นี่ คุณไม่สามารถตั้งเขตเวลาของวัตถุ juDate ที่ยกเว้นโดยการเปลี่ยนเขตเวลาเริ่มต้นของ JVM TimeZone.setDefault(TimeZone.getTimeZone("NEW_TIME_ZONE"));
โดยการเรียก ดังนั้นจึงมีการชดเชยเขตเวลาและคุณสามารถรับการชดเชยได้ด้วยการเรียกวิธีการที่เลิกใช้แล้ว juDate.getTimezoneOffset ()
toString
วิธีที่ใช้กับเขตเวลาเริ่มต้นปัจจุบันของ JVM ละเว้นโซนเวลาที่ฝังอีกครั้ง ดังนั้นเพื่อความกระชับเราบอกว่า java.util.Date ไม่มีเขตเวลา เช่นเดียวกับศิลปะมันเป็นเรื่องโกหกที่บอกความจริง
TimeZone.setDefault
คุณไม่ได้ตั้งค่าเขตเวลาของวัตถุ java.util.Date - วัตถุวันที่ยังคงละเว้นเขตเวลาฝังมันทำหน้าที่อย่างมีประสิทธิภาพใน UTC คุณจะส่งผลต่อtoString
วิธีการของวันที่ การตั้งค่าเริ่มต้นจะเปลี่ยนเขตเวลาเริ่มต้นของ JVM ซึ่งโดยปกติจะถูกตั้งค่าเป็นเขตเวลาของระบบปฏิบัติการโฮสต์ ไม่แนะนำให้โทรดังกล่าวเนื่องจากจะมีผลกับรหัสทั้งหมดในเธรดทั้งหมดของแอปทั้งหมดที่ทำงานใน JVM นั้นและดำเนินการได้ทันทีขณะดำเนินการ เป็นสิ่งที่หยาบคายและอันตรายการโทรนั้นควรถือเป็นทางเลือกสุดท้าย
equals
, hashcode
, getTime
.. ) ถ้าคุณดูที่เป็นequals
วิธีการที่เรียกว่าgetTime()
ซึ่งสายgetTimeImpl()
ที่เรียกnormalize()
ถ้าcdate
คุณสมบัติไม่ได้เป็นปกติ ในnormalize()
วิธีสุดท้ายถ้าเงื่อนไขคำนวณมิลลิวินาทีอีกครั้งนับตั้งแต่ 1/1/70 ตามข้อมูลเขตเวลาที่เก็บไว้หากเขตเวลาของcdate
แตกต่างจากเขตเวลาของสภาพแวดล้อม JVM ปัจจุบันที่กำลังทำงานอยู่ (ดูที่sun.util.calendar.AbstractCalendar getCalendarDate(long millis, CalendarDate date)
)
คุณสามารถตั้งค่าเขตเวลาที่ระดับ JVM
Date date1 = new Date();
System.out.println(date1);
TimeZone.setDefault(TimeZone.getTimeZone("UTC"));
// or pass in a command line arg: -Duser.timezone="UTC"
Date date2 = new Date();
System.out.println(date2);
เอาท์พุท:
Thu Sep 05 10:11:12 EDT 2013
Thu Sep 05 14:11:12 UTC 2013
TimeZone.setDefault
ค่อนข้างรุนแรงเนื่องจากมีผลกับ JVM ทั้งหมดมีผลกับวัตถุและเธรดอื่นทั้งหมด ดูคำตอบนี้สำหรับรายละเอียดรวมถึงความยุ่งยากที่มากขึ้นหากคุณใช้งาน SecurityManager การเพิ่มความซับซ้อนยิ่งขึ้น: พฤติกรรมนี้มีการเปลี่ยนแปลงใน Java เวอร์ชันต่างๆตามที่กล่าวไว้ในคำถามนี้
หากคุณต้องทำงานกับคลาส JDK มาตรฐานเท่านั้นคุณสามารถใช้สิ่งนี้:
/**
* Converts the given <code>date</code> from the <code>fromTimeZone</code> to the
* <code>toTimeZone</code>. Since java.util.Date has does not really store time zome
* information, this actually converts the date to the date that it would be in the
* other time zone.
* @param date
* @param fromTimeZone
* @param toTimeZone
* @return
*/
public static Date convertTimeZone(Date date, TimeZone fromTimeZone, TimeZone toTimeZone)
{
long fromTimeZoneOffset = getTimeZoneUTCAndDSTOffset(date, fromTimeZone);
long toTimeZoneOffset = getTimeZoneUTCAndDSTOffset(date, toTimeZone);
return new Date(date.getTime() + (toTimeZoneOffset - fromTimeZoneOffset));
}
/**
* Calculates the offset of the <code>timeZone</code> from UTC, factoring in any
* additional offset due to the time zone being in daylight savings time as of
* the given <code>date</code>.
* @param date
* @param timeZone
* @return
*/
private static long getTimeZoneUTCAndDSTOffset(Date date, TimeZone timeZone)
{
long timeZoneDSTOffset = 0;
if(timeZone.inDaylightTime(date))
{
timeZoneDSTOffset = timeZone.getDSTSavings();
}
return timeZone.getRawOffset() + timeZoneDSTOffset;
}
เครดิตไปที่โพสต์นี้
java.util.Calendar
เป็นวิธีปกติในการจัดการโซนเวลาโดยใช้คลาส JDK เท่านั้น Apache Commonsมีทางเลือก / ยูทิลิตี้เพิ่มเติมที่อาจมีประโยชน์ แก้ไขบันทึกย่อของ Spong ทำให้ฉันนึกถึงว่าฉันเคยได้ยินสิ่งดีๆเกี่ยวกับJoda-Time (แม้ว่าฉันจะไม่ได้ใช้มันเอง)
Period
, และDuration
Interval
ครอบคลุมเหล่านั้นรวมถึงวิธีการเปรียบเทียบเช่นcontains
, abuts
, และoverlap
gap
และPeriodFormatterBuilder
สามารถสร้างวลีที่สื่อความหมายเช่น "15 ปี 8 เดือน"
แปลง Date เป็น String และทำได้ด้วย SimpleDateFormat
SimpleDateFormat readFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
readFormat.setTimeZone(TimeZone.getTimeZone("GMT" + timezoneOffset));
String dateStr = readFormat.format(date);
SimpleDateFormat writeFormat = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss");
Date date = writeFormat.parse(dateStr);
หากใครต้องการสิ่งนี้ถ้าคุณต้องการแปลงXMLGregorianCalendar
เขตเวลาเป็นเขตเวลาปัจจุบันของคุณจาก UTC สิ่งที่คุณต้องทำคือตั้งค่าเขตเวลาเป็น0
จากนั้นโทรtoGregorianCalendar()
- มันจะยังคงอยู่ในเขตเวลาเดียวกัน แต่Date
รู้วิธีการแปลงเป็น ของคุณเพื่อให้คุณสามารถรับข้อมูลจากที่นั่น
XMLGregorianCalendar xmlStartTime = DatatypeFactory.newInstance()
.newXMLGregorianCalendar(
((GregorianCalendar)GregorianCalendar.getInstance());
xmlStartTime.setTimezone(0);
GregorianCalendar startCalendar = xmlStartTime.toGregorianCalendar();
Date startDate = startCalendar.getTime();
XMLGregorianCalendar xmlStartTime = DatatypeFactory.newInstance()
.newXMLGregorianCalendar(startCalendar);
xmlStartTime.setHour(startDate.getHours());
xmlStartTime.setDay(startDate.getDate());
xmlStartTime.setMinute(startDate.getMinutes());
xmlStartTime.setMonth(startDate.getMonth()+1);
xmlStartTime.setTimezone(-startDate.getTimezoneOffset());
xmlStartTime.setSecond(startDate.getSeconds());
xmlStartTime.setYear(startDate.getYear() + 1900);
System.out.println(xmlStartTime.toString());
ผลลัพธ์:
2015-08-26T12:02:27.183Z
2015-08-26T14:02:27.183+02:00
รหัสนี้มีประโยชน์ในแอพที่ฉันใช้งาน:
Instant date = null;
Date sdf = null;
String formatTemplate = "EEE MMM dd yyyy HH:mm:ss";
try {
SimpleDateFormat isoFormat = new SimpleDateFormat("EEE MMM dd yyyy HH:mm:ss");
isoFormat.setTimeZone(TimeZone.getTimeZone(ZoneId.of("US/Pacific")));
sdf = isoFormat.parse(timeAtWhichToMakeAvailable);
date = sdf.toInstant();
} catch (Exception e) {
System.out.println("did not parse: " + timeAtWhichToMakeAvailable);
}
LOGGER.info("timeAtWhichToMakeAvailable: " + timeAtWhichToMakeAvailable);
LOGGER.info("sdf: " + sdf);
LOGGER.info("parsed to: " + date);