รูปแบบวันที่นี้คืออะไร 2011-08-12T20: 17: 46.384Z


383

ฉันมีวันที่ดังต่อไปนี้: 2011-08-12T20:17:46.384Z. รูปแบบนี้คืออะไร ฉันพยายามแยกวิเคราะห์ด้วย Java 1.4 ผ่านทางDateFormat.getDateInstance().parse(dateStr)และฉันจะได้รับ

java.text.ParseException: วันที่ไม่สามารถค้นหา: "2011-08-12T20: 17: 46.384Z"

ฉันคิดว่าฉันควรใช้SimpleDateFormatสำหรับการวิเคราะห์คำ แต่ฉันต้องรู้สตริงรูปแบบก่อน ทั้งหมดที่ฉันมีคือจนถึงตอนนี้yyyy-MM-ddเพราะฉันไม่รู้ความTหมายในสตริงนี้ - บางสิ่งเกี่ยวข้องกับเขตเวลา สตริงวันนี้จะมาจากlcmis:downloadedOnแท็กที่แสดงบนไฟล์ CMIS ประเภทสื่อดาวน์โหลดประวัติศาสตร์


43
มันคือISO 8601
Tomasz Nurkiewicz

3
@TomaszNurkiewicz มันไม่ใช่ ISO8601 ไม่ได้มี Z ในตอนท้าย
t1gor

4
ISO8601 อนุญาตให้มี Z ที่สิ้นสุด ดูลิงค์ด้านบนค้นหา UTC
Jonathan Rosenne

2
@ t1gor Zในตอนท้ายสั้นสำหรับZuluและวิธีการUTC รูปแบบนี้แน่นอนที่สุดเป็นส่วนหนึ่งของคอลเลกชันISO 8601ของรูปแบบข้อความวันเวลามาตรฐาน อย่างไรก็ตามรูปแบบมาตรฐานเหล่านี้จะถูกใช้เป็นค่าเริ่มต้นในคลาสjava.time
Basil Bourque

3
FYI, ลำบากเก่าชั้นเรียนวันที่เวลาเช่นjava.util.Date, java.util.Calendarและjava.text.SimpleDateFormatตอนนี้มรดกแทนที่โดยjava.timeชั้นเรียนที่สร้างขึ้นใน Java 8 และ Java 9 โปรดดูที่การสอนโดยออราเคิล
Basil Bourque

คำตอบ:


512

T เป็นเพียงตัวอักษรเพื่อแยกวันที่ออกจากเวลาและ Z หมายถึง "offset zero zero" หรือที่เรียกว่า "Zulu time" (UTC) หากสตริงของคุณมี "Z" อยู่เสมอคุณสามารถใช้:

SimpleDateFormat format = new SimpleDateFormat(
    "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'", Locale.US);
format.setTimeZone(TimeZone.getTimeZone("UTC"));

หรือใช้Joda เวลาISODateTimeFormat.dateTime()คุณสามารถใช้


7
ทำไมเราต้องมีเครื่องหมายคำพูดเดี่ยว ๆTและZ?
Maroun

7
@MarounMaroun: โดยทั่วไปเราต้องการผู้ที่ตัวอักษรตัวอักษร อาจไม่จำเป็นสำหรับT(ฉันจำไม่ได้ว่า SimpleDateFormat จัดการตัวระบุที่ไม่รู้จัก) แต่Zเราต้องการให้เป็นอักขระ 'Z' แทนที่จะเป็น "ค่าออฟเซ็ต UTC" (เช่น "00")
Jon Skeet

2
@nyedidikeke: ในหน้า Wikipedia ที่คุณเชื่อมโยงไปถึงมันจะแสดง "เขตเวลา Zulu" สำหรับ UTC ฉันไม่แน่ใจว่าสิ่งที่คุณเชื่อว่าคุณกำลังแก้ไข
Jon Skeet

9
@ JonSkeet: มันอาจสร้างการอภิปรายที่ไม่จำเป็น ไม่โต้แย้งคำตอบของคุณ แต่ตั้งใจดึงความสนใจZซึ่งได้รับจดหมายเริ่มต้นจาก "zero UTC offset" ตัว Z จะเรียกว่าเป็น "ซูลู" ในสัทอักษรนาโต ในทางกลับกันวิธีการทางทหารในการอ้างถึงค่าชดเชย UTC เป็นศูนย์จะยึดตามตัวอักษร Z ซึ่งพวกเขาระบุว่าเป็นซูลูได้รับชื่อรหัสของพวกเขา: เขตเวลาซูลู เป็นสิ่งสำคัญที่จะต้องทราบว่า Z ไม่ได้สูญเสียความหมายของมันและยังคงเป็นตัวกำหนดเขตสำหรับ UTC ที่เป็นศูนย์ชดเชยเนื่องจากเขตเวลาของซูลู (จาก Z) เป็นเพียงภาษารหัสที่สืบทอดมาเพื่ออ้างถึง
nyedidikeke

2
@nyididikeke: ฉันยังคงไม่เห็นด้วยเกี่ยวกับว่ามีคนอื่นสนใจเกี่ยวกับความแตกต่างโดยอ้างอิงจากคำตอบของฉันหรือไม่ แต่ฉันได้อัปเดตแล้ว ฉันจะไม่เข้าไปดูรายละเอียดทั้งหมดเนื่องจากประวัติไม่เกี่ยวข้องกับคำตอบในวงกว้าง
Jon Skeet

86

TL; DR

สตริงอินพุตของคุณใช้รูปแบบมาตรฐานISO 8601

Instant.parse ( "2011-08-12T20:17:46.384Z" ) 

ISO 8601

รูปแบบนี้ถูกกำหนดโดยมาตรฐานการปฏิบัติที่สมเหตุสมผลISO 86018601

Tแยกส่วนวันที่จากส่วนที่เป็นเวลาของวัน Zที่สิ้นสุดหมายความUTC (นั่นคือชดเชยจากที่ UTC ศูนย์ชั่วโมงนาทีวินาที) Zจะออกเสียง“ซูลู”ออกเสียง“ซูลู”

java.time

คลาสวันที่และเวลาเก่าที่มาพร้อมกับ Java เวอร์ชันแรกสุดได้พิสูจน์แล้วว่าได้รับการออกแบบไม่ดีสับสนและลำบาก หลีกเลี่ยงพวกเขา

ให้ใช้เฟรมเวิร์กjava.time ที่สร้างขึ้นใน Java 8 และใหม่กว่า คลาส java.time แทนที่คลาสวันที่เก่าและไลบรารี Joda-Time ที่ประสบความสำเร็จอย่างสูง

คลาส java.time ใช้ISO 8601โดยค่าเริ่มต้นเมื่อแยกวิเคราะห์ / สร้างข้อความที่เป็นตัวแทนของค่าวันที่และเวลา

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

Instant instant = Instant.parse ( "2011-08-12T20:17:46.384Z" ) ;

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


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

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

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

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

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

  • Java SE 8 , Java SE 9และใหม่กว่า
    • Built-in
    • เป็นส่วนหนึ่งของ Java API มาตรฐานที่มีการใช้งานแบบรวม
    • Java 9 เพิ่มคุณสมบัติและการแก้ไขเล็กน้อย
  • Java SE 6และ Java SE 7
    • มากของการทำงาน java.time จะกลับรังเพลิง Java 6 และ 7 ในThreeTen-ย้ายกลับ
  • Android
    • การใช้งานชุดบันเดิล Android รุ่นต่อมาของคลาส java.time
    • สำหรับก่อนหน้านี้ของ Android ที่ThreeTenABPโครงการปรับThreeTen-ย้ายกลับ (ดังกล่าวข้างต้น) ดูวิธีการใช้ ThreeTenABP … .

ตารางที่ไลบรารี java.time ใดที่จะใช้กับเวอร์ชันของ Java หรือ Android

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


3
@star ว่า“ซูลู” มาจากทหารและการบินประเพณีที่ 25 ตัวอักษรของตัวอักษร AZ (ไม่ "J") ตัวอักษรที่มีชื่อออกเสียงแต่ละหมายถึงรุ่นของพวกเขาโซนเวลา โซน "ซูลู" จะถูกชดเชยด้วยเวลาศูนย์จาก UTC ดูนี้และนี้
Basil Bourque

27

ไม่แน่ใจเกี่ยวกับการแยกวิเคราะห์ Java แต่นั่นคือ ISO8601: http://en.wikipedia.org/wiki/ISO_8601


นี่คือ "2016-01-27T17: 44: 55UTC", ISO8601 ด้วยหรือไม่
user1997292

ฉันไม่เชื่ออย่างนั้น ปิดแล้ว แต่ไม่อนุญาตให้ใช้ส่วนต่อท้ายUTC จะต้องเป็น Z หรือออฟเซ็ตเขตเวลาเช่น +0100 แม้ว่า Z และ UTC จะมีความหมายเหมือนกันดังนั้นการเปลี่ยนUTCเป็นZจะให้ ISO 8601 ที่ถูกต้อง
smparkes

9

มีวิธีอื่นในการแยกวิเคราะห์แทนที่จะตอบคำถามแรก ในการแยกวิเคราะห์:

(1) หากคุณต้องการดึงข้อมูลเกี่ยวกับวันที่และเวลาคุณสามารถวิเคราะห์ข้อมูลไปยังวัตถุZonedDatetime(ตั้งแต่Java 8 ) หรือDate(เก่า) วัตถุ:

// ZonedDateTime's default format requires a zone ID(like [Australia/Sydney]) in the end.
// Here, we provide a format which can parse the string correctly.
DateTimeFormatter dtf = DateTimeFormatter.ISO_DATE_TIME;
ZonedDateTime zdt = ZonedDateTime.parse("2011-08-12T20:17:46.384Z", dtf);

หรือ

// 'T' is a literal.
// 'X' is ISO Zone Offset[like +01, -08]; For UTC, it is interpreted as 'Z'(Zero) literal.
String pattern = "yyyy-MM-dd'T'HH:mm:ss.SSSX";

// since no built-in format, we provides pattern directly.
DateFormat df = new SimpleDateFormat(pattern);

Date myDate = df.parse("2011-08-12T20:17:46.384Z");

(2) หากคุณไม่สนใจวันที่และเวลาและเพียงต้องการที่จะปฏิบัติต่อข้อมูลเป็นช่วงเวลาในหน่วยนาโนวินาทีคุณสามารถใช้Instant:

// The ISO format without zone ID is Instant's default.
// There is no need to pass any format.
Instant ins = Instant.parse("2011-08-12T20:17:46.384Z");

1

หากคุณกำลังมองหาวิธีแก้ปัญหาสำหรับ Android คุณสามารถใช้รหัสต่อไปนี้เพื่อรับวินาทีเวลาจากสตริงการประทับเวลา

public static long timestampToEpochSeconds(String srcTimestamp) {
    long epoch = 0;

    try {
        if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.O) {
            Instant instant = Instant.parse(srcTimestamp);
            epoch = instant.getEpochSecond();
        } else {
            SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd'T'hh:mm:ss.SSSSSS'Z'", Locale.getDefault());
            sdf.setTimeZone(TimeZone.getTimeZone("UTC"));
            Date date = sdf.parse(srcTimestamp);
            if (date != null) {
                epoch = date.getTime() / 1000;
            }
        }
    } catch (Exception e) {
        e.printStackTrace();
    }

    return epoch;
}

ตัวอย่างอินพุต: 2019-10-15T05: 51: 31.537979Z

ตัวอย่างผลลัพธ์: 1571128673


0

คุณสามารถใช้ตัวอย่างต่อไปนี้

    String date = "2011-08-12T20:17:46.384Z";

    String inputPattern = "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'";

    String outputPattern = "yyyy-MM-dd HH:mm:ss";

    LocalDateTime inputDate = null;
    String outputDate = null;


    DateTimeFormatter inputFormatter = DateTimeFormatter.ofPattern(inputPattern, Locale.ENGLISH);
    DateTimeFormatter outputFormatter = DateTimeFormatter.ofPattern(outputPattern, Locale.ENGLISH);

    inputDate = LocalDateTime.parse(date, inputFormatter);
    outputDate = outputFormatter.format(inputDate);

    System.out.println("inputDate: " + inputDate);
    System.out.println("outputDate: " + outputDate);

คุณไม่ควรจะใส่ apostrophes Zรอบ นั่นหมายถึงการคาดหวัง แต่ไม่สนใจจดหมายฉบับนั้น แต่จดหมายนั้นไม่ควรเพิกเฉย จดหมายนั้นให้ข้อมูลที่มีค่าความจริงที่ว่าสตริงนั้นมีไว้สำหรับ UTC ซึ่งเป็นออฟเซ็ตของศูนย์ รูปแบบของคุณกำลังยกเลิกความจริงที่สำคัญนี้ นอกจากนี้ไม่จำเป็นต้องกังวลกับการกำหนดรูปแบบการจัดรูปแบบนี้อีก ฟอร์แมตสำหรับรูปแบบนี้ถูกสร้างขึ้นมา
Basil Bourque

-1

เทคนิคนี้แปล java.util.Date เป็นรูปแบบ UTC (หรืออื่น ๆ ) และกลับมาอีกครั้ง

กำหนดคลาสดังนี้

import java.util.Date;

import org.joda.time.DateTime;
import org.joda.time.format.DateTimeFormat;
import org.joda.time.format.DateTimeFormatter;

public class UtcUtility {

public static DateTimeFormatter UTC = DateTimeFormat.forPattern("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'").withZoneUTC();


public static Date parse(DateTimeFormatter dateTimeFormatter, String date) {
    return dateTimeFormatter.parseDateTime(date).toDate();
}

public static String format(DateTimeFormatter dateTimeFormatter, Date date) {
    return format(dateTimeFormatter, date.getTime());
}

private static String format(DateTimeFormatter dateTimeFormatter, long timeInMillis) {
    DateTime dateTime = new DateTime(timeInMillis);
    String formattedString = dateTimeFormatter.print(dateTime);
    return formattedString;
}

}

จากนั้นใช้แบบนี้:

Date date = format(UTC, "2020-04-19T00:30:07.000Z")

หรือ

String date = parse(UTC, new Date())

คุณสามารถกำหนดรูปแบบวันที่อื่น ๆ ได้หากคุณต้องการ (ไม่ใช่เฉพาะ UTC)


ทั้งjava.util.DateโครงการJoda-Timeถูกแทนที่เมื่อหลายปีก่อนโดยคลาสjava.timeสมัยใหม่ที่กำหนดไว้ใน JSR 310 คำแนะนำที่นี่ล้าสมัยไปนาน
Basil Bourque

java.time ถูกนำมาใช้ใน Java 8 คำถามนี้อ้างอิง Java 1.4 โดยเฉพาะและดังนั้นฉันจึงได้จงใจหลีกเลี่ยงการใช้คลาสที่ใหม่กว่า วิธีการแก้ปัญหานี้เหมาะสำหรับฐานรหัสเก่า ฉันคิดว่าผู้เขียนสามารถย้าย codebase ของเขาไปยัง Java 8 ได้ แต่นั่นไม่ได้ตรงไปตรงมามีประสิทธิภาพหรือจำเป็นเสมอไป
Gapmeister66

-1

@ John-Skeet ให้ฉันเบาะแสเพื่อแก้ไขปัญหาของฉันเองรอบนี้ ในฐานะโปรแกรมเมอร์อายุน้อยปัญหาเล็ก ๆ นี้เป็นเรื่องง่ายที่จะพลาดและยากที่จะวินิจฉัย ดังนั้นฉันแบ่งปันมันด้วยความหวังว่ามันจะช่วยใครซักคน

ปัญหาของฉันคือฉันต้องการแยกสตริงต่อไปนี้ที่มีการประทับเวลาจาก JSON ฉันไม่มีอิทธิพลและวางไว้ในตัวแปรที่มีประโยชน์มากขึ้น แต่ฉันยังคงได้รับข้อผิดพลาด

เพื่อให้ได้รับดังต่อไปนี้ (ให้ความสนใจกับพารามิเตอร์สตริงภายใน ofPattern ();

String str = "20190927T182730.000Z"

LocalDateTime fin;
fin = LocalDateTime.parse( str, DateTimeFormatter.ofPattern("yyyyMMdd'T'HHmmss.SSSZ") );

ข้อผิดพลาด:

Exception in thread "main" java.time.format.DateTimeParseException: Text 
'20190927T182730.000Z' could not be parsed at index 19

ปัญหา? Z ที่ส่วนท้ายของรูปแบบจำเป็นต้องห่อด้วย 'Z' เหมือนกับ 'T' เปลี่ยน "yyyyMMdd'T'HHmmss.SSSZ"เป็น "yyyyMMdd'T'HHmmss.SSS'Z'"และใช้งานได้

การลบ Z ออกจากรูปแบบทั้งหมดยังนำไปสู่ข้อผิดพลาด

ตรงไปตรงมาฉันคาดว่าคลาส Java จะคาดหวังนี้


1
นี้ได้รับการถามและตอบที่นี่และที่นี่ และทางออกของคุณผิด ในขณะที่Tเป็นตัวอักษรและจะต้องมีการเสนอราคาZเป็นชดเชย (จากศูนย์) และจะต้องมีการแยกวิเคราะห์เช่นนี้หรือคุณจะได้รับผลลัพธ์ที่ผิด ฉันไม่รู้ว่าคุณหมายความว่ายังไม่ได้รับการคาดหวังฉันเชื่อว่ามันมี
Ole VV

1
Zไม่ไม่ไม่ไม่ไม่สนใจ คุณกำลังละทิ้งข้อมูลที่มีค่า การประมวลผลค่าวันที่ในขณะที่ละเว้นเขตเวลาหรือออฟเซ็ตจาก UTC เป็นเหมือนการประมวลผลจำนวนเงินในขณะที่ละเว้นสกุลเงิน!
Basil Bourque

1
Tเพียงแยกส่วนวันที่จากส่วนที่เป็นเวลาของวันและเพิ่มไม่มีความหมาย Zในมืออื่น ๆ แน่นอนเพิ่มความหมาย
Basil Bourque

โอขอบคุณสำหรับลิงค์ ป่วยอ่านอย่างแน่นอน และฉันยอมรับว่าฉันอาจผิดอย่างแน่นอนในฐานะคุณ ทั้งหมดที่ฉันพูดคือการตัด Z ในเครื่องหมายคำพูดเดี่ยวเช่นอักขระในรูปแบบสามารถแก้ไขข้อผิดพลาดได้ คำติชมของฉันคือใครก็ตามที่ออกแบบ "รูปแบบ" ส่วนหนึ่งของคลาสอาจมีรหัสที่มีหรือไม่มี 'Z' (หรือเขตเวลาต่างกัน?) และ 'T' ถูกล้อมด้วย '' หรือไม่ เพราะพวกเขาไม่ได้มีลักษณะเฉพาะ รูปแบบการจัดการกับมาจาก JSON จาก API เชิงพาณิชย์แยกเป็นสตริง แต่ฉันต้องแยกวิเคราะห์สิ่งนั้นลงในปฏิทินวันที่ ฯลฯ เพื่อให้มีประโยชน์มากขึ้น
spencemw
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.