ฉันมาจากโลก C # ดังนั้นยังไม่เคยมีประสบการณ์กับ Java ฉันเพิ่งบอกโดย Eclipse ที่Date
เลิกใช้แล้ว:
Person p = new Person();
p.setDateOfBirth(new Date(1985, 1, 1));
ทำไม? และควรใช้อะไร (โดยเฉพาะในกรณีเช่นด้านบน) แทน?
ฉันมาจากโลก C # ดังนั้นยังไม่เคยมีประสบการณ์กับ Java ฉันเพิ่งบอกโดย Eclipse ที่Date
เลิกใช้แล้ว:
Person p = new Person();
p.setDateOfBirth(new Date(1985, 1, 1));
ทำไม? และควรใช้อะไร (โดยเฉพาะในกรณีเช่นด้านบน) แทน?
คำตอบ:
ตัวDate
สร้างเฉพาะเลิกใช้แล้วและCalendar
ควรใช้แทน JavaDoc
สำหรับวันที่Calendar
จะอธิบายถึงการก่อสร้างจะเลิกใช้และวิธีการแทนที่พวกเขาใช้
java.util.Date
, java.util.Calendar
และjava.text.SimpleDateFormat
ตอนนี้มรดกแทนที่โดยjava.timeชั้นเรียนที่สร้างขึ้นใน Java 8 และต่อมา ดูการสอนโดยออราเคิล
java.util.Date
ชั้นไม่ได้เลิกใช้จริงเพียงแค่สร้างที่พร้อมกับคู่อื่น ๆ ก่อสร้าง / วิธีการถูกปฏิเสธ มันเลิกใช้แล้วเนื่องจากการใช้งานประเภทนั้นใช้งานไม่ได้กับระบบสากล Calendar
ชั้นควรจะใช้แทน:
Calendar cal = Calendar.getInstance();
cal.set(Calendar.YEAR, 1988);
cal.set(Calendar.MONTH, Calendar.JANUARY);
cal.set(Calendar.DAY_OF_MONTH, 1);
Date dateRepresentation = cal.getTime();
ดูวันที่ Javadoc:
http://download.oracle.com/javase/6/docs/api/java/util/Date.html
Calendar cal = Calendar.getInstance(TimeZone.getTimeZone(<timezone id>));
java.time.*
คลาสนั้นเป็นตัวเลือกที่ดีกว่า ... หากคุณเปลี่ยนรหัส
LocalDate.of( 1985 , 1 , 1 )
…หรือ…
LocalDate.of( 1985 , Month.JANUARY , 1 )
The java.util.Date
, java.util.Calendar
และjava.text.SimpleDateFormat
คลาสต่างรีบเร่งเร็วเกินไปเมื่อ Java เปิดตัวและพัฒนาครั้งแรก ชั้นเรียนไม่ได้รับการออกแบบหรือใช้งานอย่างดี มีการพยายามปรับปรุงทำให้เกิดความไม่พอใจที่คุณพบ น่าเสียดายที่ความพยายามในการปรับปรุงล้มเหลวเป็นส่วนใหญ่ คุณควรหลีกเลี่ยงคลาสเหล่านี้ทั้งหมด พวกมันถูกแทนที่ด้วย Java 8 โดยคลาสใหม่
java.util.Date มีทั้งวันที่และเวลา คุณละเว้นส่วนเวลาในรหัสของคุณ ดังนั้นคลาส Date จะเริ่มต้นวันตามที่กำหนดโดยเขตเวลาเริ่มต้นของ JVM ของคุณและใช้เวลานั้นกับวัตถุ Date ดังนั้นผลลัพธ์ของรหัสของคุณจะแตกต่างกันไปขึ้นอยู่กับเครื่องที่มันทำงานหรือตั้งค่าเขตเวลา อาจไม่ใช่สิ่งที่คุณต้องการ
หากคุณต้องการเพียงแค่วันที่โดยไม่มีส่วนเวลาเช่นวันเกิดคุณอาจไม่ต้องการใช้Date
วัตถุ คุณอาจต้องการที่จะเก็บเพียงสายของวันที่ในการรับรองมาตรฐาน ISO 8601YYYY-MM-DD
รูปแบบของ หรือใช้LocalDate
วัตถุจาก Joda-Time (ดูด้านล่าง)
สิ่งแรกที่ต้องเรียนรู้ใน Java: หลีกเลี่ยงคลาส java.util.Date ที่มีปัญหาอย่างฉาวโฉ่และ java.util.Calendar ที่มาพร้อมกับ Java
ตามที่ระบุไว้อย่างถูกต้องในคำตอบโดย user3277382ให้ใช้แพ็คเกจJoda-Timeหรือjava.time. *ใหม่ใน Java 8
DateTimeZone timeZoneNorway = DateTimeZone.forID( "Europe/Oslo" );
DateTime birthDateTime_InNorway = new DateTime( 1985, 1, 1, 3, 2, 1, timeZoneNorway );
DateTimeZone timeZoneNewYork = DateTimeZone.forID( "America/New_York" );
DateTime birthDateTime_InNewYork = birthDateTime_InNorway.toDateTime( timeZoneNewYork );
DateTime birthDateTime_UtcGmt = birthDateTime_InNorway.toDateTime( DateTimeZone.UTC );
LocalDate birthDate = new LocalDate( 1985, 1, 1 );
ถ่ายโอนข้อมูลไปยังคอนโซล ...
System.out.println( "birthDateTime_InNorway: " + birthDateTime_InNorway );
System.out.println( "birthDateTime_InNewYork: " + birthDateTime_InNewYork );
System.out.println( "birthDateTime_UtcGmt: " + birthDateTime_UtcGmt );
System.out.println( "birthDate: " + birthDate );
เมื่อวิ่ง ...
birthDateTime_InNorway: 1985-01-01T03:02:01.000+01:00
birthDateTime_InNewYork: 1984-12-31T21:02:01.000-05:00
birthDateTime_UtcGmt: 1985-01-01T02:02:01.000Z
birthDate: 1985-01-01
ในกรณีนี้รหัสสำหรับjava.timeเกือบจะเหมือนกับที่Joda เวลา
เราได้รับเขตเวลา ( ZoneId
) และสร้างวัตถุวันที่ที่กำหนดให้กับเขตเวลานั้น ( ZonedDateTime
) แล้วใช้ไม่เปลี่ยนรูปวัตถุรูปแบบที่เราสร้างวันครั้งใหม่บนพื้นฐานทันทีเดียวกันวัตถุเก่า (นับของนาโนวินาทีตั้งแต่ยุค ) แต่ได้รับมอบหมายโซนเวลาอื่น ๆ สุดท้ายเราได้รับLocalDate
ซึ่งไม่มีเวลาของวันหรือโซนเวลาแม้ว่าจะแจ้งให้ทราบล่วงหน้าเขตเวลาที่ใช้ในการกำหนดวันที่ (เป็นอรุณรุ่งวันใหม่ก่อนหน้านี้ในออสโลกว่าในนิวยอร์กตัวอย่าง)
ZoneId zoneId_Norway = ZoneId.of( "Europe/Oslo" );
ZonedDateTime zdt_Norway = ZonedDateTime.of( 1985 , 1 , 1 , 3 , 2 , 1 , 0 , zoneId_Norway );
ZoneId zoneId_NewYork = ZonedId.of( "America/New_York" );
ZonedDateTime zdt_NewYork = zdt_Norway.withZoneSameInstant( zoneId_NewYork );
ZonedDateTime zdt_Utc = zdt_Norway.withZoneSameInstant( ZoneOffset.UTC ); // Or, next line is similar.
Instant instant = zdt_Norway.toInstant(); // Instant is always in UTC.
LocalDate localDate_Norway = zdt_Norway.toLocalDate();
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
และอื่น ๆ อีกมากมาย
เหตุผลหนึ่งที่ตัวสร้างถูกคัดค้านคือความหมายของพารามิเตอร์ปีไม่ใช่สิ่งที่คุณคาดหวัง javadoc พูดว่า:
ในฐานะของ JDK รุ่น 1.1
Calendar.set(year + 1900, month, date)
แทนที่ด้วย
โปรดสังเกตว่าฟิลด์ปีคือจำนวนปี1900
ดังนั้นรหัสตัวอย่างของคุณน่าจะไม่ทำตามที่คุณคาดหวัง และนั่นคือประเด็น
โดยทั่วไปแล้วDate
API สนับสนุนเฉพาะปฏิทินตะวันตกที่ทันสมัยมีองค์ประกอบที่ระบุเฉพาะและมีพฤติกรรมไม่สอดคล้องกันหากคุณตั้งค่าฟิลด์
Calendar
และGregorianCalendar
APIs จะดีกว่าDate
และบุคคลที่ 3 APIs Joda เวลาที่ถูกคิดโดยทั่วไปจะดีที่สุด ใน Java 8 พวกเขาแนะนำjava.time
แพ็คเกจและตอนนี้เป็นทางเลือกที่แนะนำ
ฉันเจอคำถามนี้เป็นคำถามที่ใหม่กว่าซึ่งถามว่าวิธีที่ไม่ได้รับการปฏิเสธในการรับDate
ปีเดือนและวันที่ระบุ
คำตอบที่นี่จนถึงตอนนี้บอกว่าจะใช้Calendar
คลาสและนั่นเป็นความจริงจนกระทั่ง Java 8 ออกมา แต่เป็นของ Java 8 วิธีมาตรฐานในการทำเช่นนี้คือ:
LocalDate localDate = LocalDate.of(1985, 1, 1);
และถ้าคุณต้องการจริงๆjava.util.Date
คุณสามารถใช้คำแนะนำในคำถามนี้
โปรดทราบว่าCalendar.getTime()
nondeterministic ในแง่ที่ว่าเวลากลางวันส่วนเริ่มต้นเป็นเวลาปัจจุบัน
หากต้องการทำซ้ำให้ลองเรียกใช้รหัสต่อไปนี้สองสามครั้ง:
Calendar c = Calendar.getInstance();
c.set(2010, 2, 7); // NB: 2 means March, not February!
System.err.println(c.getTime());
ผลลัพธ์เช่น:
Sun Mar 07 10:46:21 CET 2010
ใช้รหัสเดียวกันแน่นอนสองสามนาทีต่อมาอัตราผลตอบแทน:
Sun Mar 07 10:57:51 CET 2010
ดังนั้นในขณะที่set()
บังคับให้เขตข้อมูลที่เกี่ยวข้องเพื่อแก้ไขค่ามันรั่วเวลาของระบบสำหรับเขตข้อมูลอื่น (ทดสอบข้างต้นด้วย Sun jdk6 & jdk7)
Date
ตัวเองไม่ได้เลิก มันเป็นวิธีการมากมาย ดูที่นี่สำหรับรายละเอียด
ใช้java.util.Calendar
แทน
นักพัฒนา Java ส่วนใหญ่ใช้แพ็คเกจJoda-Timeของบุคคลที่สามในปัจจุบัน Jodaมันได้รับการยอมรับอย่างกว้างขวางว่าเป็นการใช้งานที่ดีกว่ามาก
อย่างไรก็ตามJava 8จะมีแพ็คเกจ java.time. *ใหม่ ดูบทความนี้แนะนำวันและเวลาใหม่ API สำหรับ JDK 8
เนื่องจากตัวสร้าง Date เลิกใช้แล้วคุณสามารถลองใช้รหัสนี้ได้
import java.util.Calendar;
Calendar calendar = Calendar.getInstance();
calendar.set(Calendar.HOUR_OF_DAY, 6);// for 6 hour
calendar.set(Calendar.MINUTE, 0);// for 0 min
calendar.set(Calendar.SECOND, 0);// for 0 sec
calendar.set(1996,0,26);// for Date [year,month(0 to 11), date]
Date date = new Date(calendar.getTimeInMillis());// calendar gives long value
String mConvertedDate = date.toString();// Fri Jan 26 06:00:00 GMT+05:30 1996
Date
และCalendar
ในปี 2019 เป็นคำแนะนำที่ไม่ดี
คล้ายกับสิ่งที่ binnyb แนะนำคุณอาจพิจารณาใช้วิธีการใหม่กว่า> วิธี GregorianCalendar ดูเอกสารล่าสุดเหล่านี้:
http://download.oracle.com/javase/6/docs/api/java/util/GregorianCalendar.html
คุณสามารถสร้างวิธีการเหมือนnew Date(year,month,date)
ในรหัสของคุณโดยใช้Calendar
คลาส
private Date getDate(int year,int month,int date){
Calendar cal = Calendar.getInstance();
cal.set(Calendar.YEAR, year);
cal.set(Calendar.MONTH, month-1);
cal.set(Calendar.DAY_OF_MONTH, day);
return cal.getTime();
}
มันจะทำงานเหมือนกับตัวสร้างที่ไม่สนับสนุนของ Date
Instant
ZonedDateTime
ตัวสร้างวันที่คาดว่าปีที่ผ่านมาในรูปแบบของปีตั้งแต่ 1900, เดือนที่เป็นศูนย์, หนึ่งวันที่ใช้และตั้งค่าชั่วโมง / นาที / วินาที / มิลลิวินาทีเป็นศูนย์
Date result = new Date(year, month, day);
ดังนั้นการใช้การแทนที่ปฏิทิน (ปีที่เป็นศูนย์, เดือนที่เป็นศูนย์, หนึ่งวันที่ใช้) สำหรับตัวสร้าง Date ที่เลิกใช้แล้วเราต้องการบางสิ่งดังนี้:
Calendar calendar = Calendar.getInstance();
calendar.clear(); // Sets hours/minutes/seconds/milliseconds to zero
calendar.set(year + 1900, month, day);
Date result = calendar.getTime();
หรือใช้ Java 1.8 (ซึ่งมีปี zero-based และหนึ่งเดือนและวัน):
Date result = Date.from(LocalDate.of(year + 1900, month + 1, day).atStartOfDay(ZoneId.systemDefault()).toInstant());
นี่คือ Date, Calendar และ Java 1.8 ที่เท่ากัน:
int year = 1985; // 1985
int month = 1; // January
int day = 1; // 1st
// Original, 1900-based year, zero-based month, one-based day
Date date1 = new Date(year - 1900, month - 1, day);
// Calendar, zero-based year, zero-based month, one-based day
Calendar calendar = Calendar.getInstance();
calendar.clear(); // Sets hours/minutes/seconds/milliseconds to zero
calendar.set(year, month - 1, day);
Date date2 = calendar.getTime();
// Java-time back to Date, zero-based year, one-based month, one-based day
Date date3 = Date.from(LocalDate.of(year, month, day).atStartOfDay(ZoneId.systemDefault()).toInstant());
SimpleDateFormat format = new SimpleDateFormat("yyyy-MMM-dd HH:mm:ss.SSS");
// All 3 print "1985-Jan-01 00:00:00.000"
System.out.println(format.format(date1));
System.out.println(format.format(date2));
System.out.println(format.format(date3));
new GregorianCalendar(1985, Calendar.JANUARY, 1).getTime();
(วิธี pre-Java-8)