一 Jode-Time 介紹java
任何企業應用程序都須要處理時間問題。應用程序須要知道當前的時間點和下一個時間點,有時它們還必須計算這兩個
時間點之間的路徑。使用 JDK 完成這項任務將很是痛苦和繁瑣。
既然沒法擺脫時間,爲什麼不設法簡化時間處理?如今來看看 Joda Time,一個面向 Java™ 平臺的易於
使用的開源時間/日期庫。正如您在本文中瞭解的那樣,JodaTime輕鬆化解了處理日期和時間的痛苦和繁瑣。算法
Joda-Time 令時間和日期值變得易於管理、操做和理解。事實上,易於使用是 Joda 的主要設計目標。其餘目標包括可擴展性、完整的特性集以及對多種日曆系統的支持。
而且 Joda 與 JDK 是百分之百可互操做的,所以您無需替換全部 Java 代碼,只須要替換執行日期/時間計算的那部分代碼。
Joda-Time提供了一組Java類包用於處理包括ISO8601標準在內的date和time。能夠利用它把JDK Date和Calendar類徹底替換掉,並且仍然可以提供很好的集成。ide
爲何要使用 Joda?
考慮建立一個用時間表示的某個隨意的時刻 — 好比,2000 年 1 月 1 日 0 時 0 分。
我如何建立一個用時間表示這個瞬間的 JDK 對象?使用 java.util.Date?
事實上這是行不通的,由於自 JDK 1.1 以後的每一個 Java 版本的 Javadoc 都聲明應當使用 java.util.Calendar。
Date 中不同意使用的構造函數的數量嚴重限制了您建立此類對象的途徑。函數
那麼 Calendar 又如何呢?我將使用下面的方式建立必需的實例:工具
Calendar calendar = Calendar.getInstance(); calendar.set(2000, Calendar.JANUARY, 1, 0, 0, 0);
使用 Joda,代碼應該相似以下所示:idea
DateTime dateTime = new DateTime(2000, 1, 1, 0, 0, 0, 0);
這一行簡單代碼沒有太大的區別。可是如今我將使問題稍微複雜化。
假設我但願在這個日期上加上 90 天並輸出結果。使用 JDK,我須要使用清單 1 中的代碼:spa
// 以 JDK 的方式向某一個瞬間加上 90 天並輸出結果 Calendar calendar = Calendar.getInstance(); calendar.set(2000, Calendar.JANUARY, 1, 0, 0, 0); SimpleDateFormat sdf = new SimpleDateFormat("E MM/dd/yyyy HH:mm:ss.SSS"); calendar.add(Calendar.DAY_OF_MONTH, 90); System.out.println(sdf.format(calendar.getTime()));
// 以 Joda 的方式向某一個瞬間加上 90 天並輸出結果 DateTime dateTime = new DateTime(2000, 1, 1, 0, 0, 0, 0); System.out.println(dateTime.plusDays(90).toString("E MM/dd/yyyy HH:mm:ss.SSS");
二者之間的差距拉大了(Joda 用了兩行代碼,JDK 則是 5 行代碼)。
如今假設我但願輸出這樣一個日期:距離 2000.1.1日 45 天以後的某天在下一個月的當前周的最後一天的日期。
坦白地說,我甚至不想使用 Calendar 處理這個問題。
使用 JDK 實在太痛苦了,即便是簡單的日期計算,好比上面這個計算。
正是多年前的這樣一個時刻,我第一次領略到 JodaTime的強大。使用 Joda,用於計算的代碼所示:設計
DateTime dateTime = new DateTime(2000, 1, 1, 0, 0, 0, 0); System.out.println(dateTime.plusDays(45).plusMonths(1).dayOfWeek() .withMaximumValue().toString("E MM/dd/yyyy HH:mm:ss.SSS"); 輸出爲: Sun 03/19/2000 00:00:00.000
若是您正在尋找一種易於使用的方式替代 JDK 日期處理,那麼您真的應該考慮 Joda。code
二 建立Joda-Time對象orm
如今,我將展現在採用該庫時會常常遇到的一些 Joda 類,並展現如何建立這些類的實例。
ReadableInstant
Joda 經過 ReadableInstant 類實現了瞬間性這一律念。表示時間上的不可變瞬間的 Joda 類都屬於這個類的子類。
(將這個類命名爲ReadOnlyInstant 可能更好,我認爲這纔是設計者須要傳達的意思)。
換句話說,ReadableInstant 表示時間上的某一個不可修改的瞬間。
其中的兩個子類分別爲 DateTime 和 DateMidnight:
DateTime:這是最經常使用的一個類。它以毫秒級的精度封裝時間上的某個瞬間時刻。
DateTime 始終與 DateTimeZone 相關,若是您不指定它的話,它將被默認設置爲運行代碼的機器所在的時區。
可使用多種方式構建 DateTime 對象。這個構造函數使用系統時間:
DateTime dateTime = new DateTime();
若是您建立了一個 DateTime 的實例,而且沒有提供 Chronology 或 DateTimeZone,Joda將使用 ISOChronology(默認)和DateTimeZone(來自系統設置)
Joda 可使您精確地控制建立 DateTime 對象的方式,該對象表示時間上的某個特定的瞬間。
1 DateTime dateTime = new DateTime( 2 2000, //year 3 1, // month 4 1, // day 5 0, // hour (midnight is zero) 6 0, // minute 7 0, // second 8 0 // milliseconds 9 );
下一個構造函數將指定從 epoch(1970年1月1日 子時 格林威治標準時間) 到某個時刻所通過的毫秒數。
它根據 JDK Date 對象的毫秒值建立一個DateTime 對象,其時間精度用毫秒錶示,由於 epoch 與 Joda 是相同的:
1 java.util.Date jdkDate = new Date(); 2 long timeInMillis = jdkDate.getTime(); 3 DateTime dateTime = new DateTime(timeInMillis);
或者Date 對象直接傳遞給構造函數:
dateTime = new DateTime(new Date());
Joda 支持使用許多其餘對象做爲構造函數的參數,用於建立 DateTime:
1 // Use a Calendar 2 dateTime = new DateTime(calendar); 3 4 // Use another Joda DateTime 5 dateTime = new DateTime(anotherDateTime); 6 7 // Use a String (must be formatted properly) 8 String timeString = "2006-01-26T13:30:00-06:00"; 9 dateTime = new DateTime(timeString); 10 timeString = "2006-01-26"; 11 dateTime = new DateTime(timeString);
注意,若是您準備使用 String(必須通過解析),您必須對其進行精確地格式化。
DateMidnight:這個類封裝某個時區(一般爲默認時區)在特定年/月/日的午夜時分的時刻。
它基本上相似於 DateTime,不一樣之處在於時間部分老是爲與該對象關聯的特定 DateTimeZone 時區的午夜時分。
ReadablePartial
應用程序所需處理的日期問題並不所有都與時間上的某個完整時刻有關,所以您能夠處理一個局部時刻。
例如,有時您比較關心年/月/日,或者一天中的時間,甚至是一週中的某天。Joda 設計者使用ReadablePartial 接口捕捉這種表示局部時間的概念,
這是一個不可變的局部時間片斷。用於處理這種時間片斷的兩個有用類分別爲 LocalDate 和 LocalTime:
LocalDate:該類封裝了一個年/月/日的組合。當地理位置(即時區)變得不重要時,使用它存儲日期將很是方便。
例如,某個特定對象的出生日期 可能爲 1999 年 4 月 16 日,可是從技術角度來看,
在保存全部業務值的同時不會了解有關此日期的任何其餘信息(好比這是一週中的星期幾,或者這我的出生地所在的時區)。
在這種狀況下,應當使用 LocalDate。
LocalTime:這個類封裝一天中的某個時間,當地理位置不重要的狀況下,可使用這個類來只存儲一天當中的某個時間。
例如,晚上 11:52 多是一天當中的一個重要時刻(好比,一個 cron 任務將啓動,它將備份文件系統的某個部分),
可是這個時間並無特定於某一天,所以我不須要了解有關這一時刻的其餘信息。
建立對象代碼:
1 package com.jt.joda; 2 3 import java.util.Date; 4 5 import org.joda.time.DateTime; 6 import org.joda.time.LocalDate; 7 import org.joda.time.LocalTime; 8 import org.junit.Test; 9 10 public class Demo { 11 12 @Test 13 public void test1(){ 14 15 //方法一:取系統點間 16 DateTime dt1 = new DateTime(); 17 System.out.println(dt1); 18 19 //方法二:經過java.util.Date對象生成 20 DateTime dt2 = new DateTime(new Date()); 21 System.out.println(dt2); 22 23 //方法三:指定年月日點分秒生成(參數依次是:年,月,日,時,分,秒,毫秒) 24 DateTime dt3 = new DateTime(2012, 5, 20, 13, 14, 0, 0); 25 System.out.println(dt3); 26 //方法四:ISO8601形式生成 27 DateTime dt4 = new DateTime("2012-05-20"); 28 System.out.println(dt4); 29 DateTime dt5 = new DateTime("2012-05-20T13:14:00"); 30 System.out.println(dt5); 31 32 //只須要年月日的時候 33 LocalDate localDate = new LocalDate(2009, 9, 6);// September 6, 2009 34 System.out.println(localDate); 35 36 //只須要時分秒毫秒的時候 37 LocalTime localTime = new LocalTime(13, 30, 26, 0);// 1:30:26PM 38 System.out.println(localTime); 39 40 } 41 /* 42 2015-09-25T17:51:12.900+08:00 43 2015-09-25T17:51:12.977+08:00 44 2012-05-20T13:14:00.000+08:00 45 2012-05-20T00:00:00.000+08:00 46 2012-05-20T13:14:00.000+08:00 47 2009-09-06 48 13:30:26.000 49 */ 50 51 }
三 與JDK日期對象轉換
許多代碼都使用了 JDK Date 和 Calendar 類。可是幸好有 Joda,能夠執行任何須要的日期算法,而後再轉換回 JDK 類。
這將二者的優勢集中到一塊兒。您在本文中看到的全部 Joda 類均可以從 JDK Calendar 或 Date 建立,正如您在 建立 JodaTime對象 中看到的那樣。
出於一樣的緣由,能夠從您所見過的任何 Joda 類建立 JDK Calendar 或 Date。
1. DateTime dt = new DateTime(); 2. 3. //轉換成java.util.Date對象 4. Date d1 = new Date(dt.getMillis()); 5. Date d2 = dt.toDate(); 6. 7. //轉換成java.util.Calendar對象 8. Calendar c1 = Calendar.getInstance(); 9. c1.setTimeInMillis(dt.getMillis()); 10. Calendar c2 = dt.toCalendar(Locale.getDefault());
對於 ReadablePartial 子類,您還須要通過額外一步,如所示:
1 Date date = localDate.toDateMidnight().toDate();
要建立 Date 對象,您必須首先將它轉換爲一個 DateMidnight 對象,而後只須要將 DateMidnight 對象做爲 Date。
(固然,產生的 Date 對象將把它本身的時間部分設置爲午夜時刻)。
JDK 互操做性被內置到 Joda API 中,所以您無需所有替換本身的接口,若是它們被綁定到 JDK 的話。比
如,您可使用 Joda 完成複雜的部分,而後使用 JDK 處理接口。
四 日期計算
如今,您已經瞭解瞭如何建立一些很是有用的 Joda 類,我將向您展現如何使用它們執行日期計算。
假設在當前的系統日期下,我但願計算上一個月的最後一天。對於這個例子,我並不關心一天中的時間,由於我只須要得到年/月/日,如所示:
LocalDate now = SystemFactory.getClock().getLocalDate(); LocalDate lastDayOfPreviousMonth = now.minusMonths(1).dayOfMonth().withMaximumValue();
首先,我從當前月份減去一個月,獲得 「上一個月」。
接着,我要求得到 dayOfMonth 的最大值,它使我獲得這個月的最後一天。
注意,這些調用被鏈接到一塊兒(注意 Joda ReadableInstant 子類是不可變的),這樣您只須要捕捉調用鏈中最後一個方法的結果,從而得到整個計算的結果。
您可能對dayOfMonth() 調用感興趣。這在 Joda 中被稱爲屬性(property)。它至關於 Java對象的屬性。
屬性是根據所表示的常見結構命名的,而且它被用於訪問這個結構,用於完成計算目的。
屬性是實現 Joda 計算威力的關鍵。您目前所見到的全部 4 個 Joda 類都具備這樣的屬性。一些例子包括:
yearOfCentury
dayOfYear
monthOfYear
dayOfMonth
dayOfWeek
假設您但願得到任何一年中的第 11 月的第一個星期二的日期,而這天必須是在這個月的第一個星期一以後。
LocalDate now = SystemFactory.getClock().getLocalDate(); LocalDate electionDate = now.monthOfYear() .setCopy(11) // November .dayOfMonth() // Access Day Of Month Property .withMinimumValue() // Get its minimum value .plusDays(6) // Add 6 days .dayOfWeek() // Access Day Of Week Property .setCopy("Monday") // Set to Monday (it will round down) .plusDays(1); // Gives us Tuesday
.setCopy("Monday") 是整個計算的關鍵。無論中間LocalDate 值是多少,將其 dayOfWeek 屬性設置爲 Monday 老是可以四捨五入,
這樣的話,在每個月的開始再加上 6 天就可以讓您獲得第一個星期一。再加上一天就獲得第一個星期二。Joda 使得執行此類計算變得很是容易。
下面是其餘一些由於使用 Joda 而變得超級簡單的計算:
1. DateTime dt = new DateTime(); 2. 3. //昨天 4. DateTime yesterday = dt.minusDays(1); 5. //明天 6. DateTime tomorrow = dt.plusDays(1); 7. //1個月前 8. DateTime before1month = dt.minusMonths(1); 9. //3個月後 10. DateTime after3month = dt.plusMonths(3); 11. //2年前 12. DateTime before2year = dt.minusYears(2); 13. //5年後 14. DateTime after5year = dt.plusYears(5);
五 格式化時間
使用 JDK 格式化日期以實現打印是徹底能夠的,可是我始終認爲它應該更簡單一些。
這是 Joda 設計者進行了改進的另外一個特性。要格式化一個 Joda 對象,調用它的 toString() 方法,
而且若是您願意的話,傳遞一個標準的 ISO8601或一個 JDK 兼容的控制字符串,以告訴 JDK 如何執行格式化。
不須要建立單獨的 SimpleDateFormat 對象
(可是 Joda 的確爲那些喜歡自找麻煩的人提供了一個DateTimeFormatter 類)。
調用 Joda 對象的 toString() 方法,僅此而已。
dateTime.toString(ISODateTimeFormat.basicDateTime()); dateTime.toString(ISODateTimeFormat.basicDateTimeNoMillis()); dateTime.toString(ISODateTimeFormat.basicOrdinalDateTime()); dateTime.toString(ISODateTimeFormat.basicWeekDateTime()); 20090906T080000.000-0500 20090906T080000-0500 2009249T080000.000-0500 2009W367T080000.000-0500
DateTime dateTime = DateTime.now(); dateTime.toString("MM/dd/yyyy hh:mm:ss.SSSa"); dateTime.toString("dd-MM-yyyy HH:mm:ss"); dateTime.toString("EEEE dd MMMM, yyyy HH:mm:ssa"); dateTime.toString("MM/dd/yyyy HH:mm ZZZZ"); dateTime.toString("MM/dd/yyyy HH:mm Z"); 09/06/2009 02:30:00.000PM 06-Sep-2009 14:30:00 Sunday 06 September, 2009 14:30:00PM 09/06/2009 14:30 America/Chicago 09/06/2009 14:30 -0500
結束語
談到日期處理,Joda 是一種使人驚奇的高效工具。不管您是計算日期、打印日期,或是解析日期,Joda都將是工具箱中的便捷工具。
在本文中,我首先介紹了 Joda,它能夠做爲 JDK 日期/時間庫的替代選擇。而後介紹了一些 Joda 概念,以及如何使用 Joda 執行日期計算和格式化。
六 使用代碼案例
二、獲取年月日點分秒 1. DateTime dt = new DateTime(); 2. //年 3. int year = dt.getYear(); 4. //月 5. int month = dt.getMonthOfYear(); 6. //日 7. int day = dt.getDayOfMonth(); 8. //星期 9. int week = dt.getDayOfWeek(); 10. //點 11. int hour = dt.getHourOfDay(); 12. //分 13. int min = dt.getMinuteOfHour(); 14. //秒 15. int sec = dt.getSecondOfMinute(); 16. //毫秒 17. int msec = dt.getMillisOfSecond(); 3 星期的特殊處理 dt.getDayOfWeek() 1. DateTime dt = new DateTime(); 2. 3. //星期 4. switch(dt.getDayOfWeek()) { 5. case DateTimeConstants.SUNDAY: 6. System.out.println("星期日"); 7. break; 8. case DateTimeConstants.MONDAY: 9. System.out.println("星期一"); 10. break; 11. case DateTimeConstants.TUESDAY: 12. System.out.println("星期二"); 13. break; 14. case DateTimeConstants.WEDNESDAY: 15. System.out.println("星期三"); 16. break; 17. case DateTimeConstants.THURSDAY: 18. System.out.println("星期四"); 19. break; 20. case DateTimeConstants.FRIDAY: 21. System.out.println("星期五"); 22. break; 23. case DateTimeConstants.SATURDAY: 24. System.out.println("星期六"); 25. break; 26. } 四、與JDK日期對象的轉換 1. DateTime dt = new DateTime(); 2. 3. //轉換成java.util.Date對象 4. Date d1 = new Date(dt.getMillis()); 5. Date d2 = dt.toDate(); 6. 7. //轉換成java.util.Calendar對象 8. Calendar c1 = Calendar.getInstance(); 9. c1.setTimeInMillis(dt.getMillis()); 10. Calendar c2 = dt.toCalendar(Locale.getDefault()); 五、日期先後推算 1. DateTime dt = new DateTime(); 2. 3. //昨天 4. DateTime yesterday = dt.minusDays(1); 5. //明天 6. DateTime tomorrow = dt.plusDays(1); 7. //1個月前 8. DateTime before1month = dt.minusMonths(1); 9. //3個月後 10. DateTime after3month = dt.plusMonths(3); 11. //2年前 12. DateTime before2year = dt.minusYears(2); 13. //5年後 14. DateTime after5year = dt.plusYears(5); 六、取特殊日期 1. DateTime dt = new DateTime(); 2. 3. //月末日期 4. DateTime lastday = dt.dayOfMonth().withMaximumValue(); 5. 6. //90天后那周的週一 7. DateTime firstday = dt.plusDays(90).dayOfWeek().withMinimumValue(); 七、時區 1. //默認設置爲日本時間 2. DateTimeZone.setDefault(DateTimeZone.forID("Asia/Tokyo")); 3. DateTime dt1 = new DateTime(); 4. 5. //倫敦時間 6. DateTime dt2 = new DateTime(DateTimeZone.forID("Europe/London")); 八、計算區間 1. DateTime begin = new DateTime("2012-02-01"); 2. DateTime end = new DateTime("2012-05-01"); 3. 4. //計算區間毫秒數 5. Duration d = new Duration(begin, end); 6. long time = d.getMillis(); 7. 8. //計算區間天數 9. Period p = new Period(begin, end, PeriodType.days()); 10. int days = p.getDays(); 11. 12. //計算特定日期是否在該區間內 13. Interval i = new Interval(begin, end); 14. boolean contained = i.contains(new DateTime("2012-03-01")); 九、日期比較 1. DateTime d1 = new DateTime("2012-02-01"); 2. DateTime d2 = new DateTime("2012-05-01"); 3. 4. //和系統時間比 5. boolean b1 = d1.isAfterNow(); 6. boolean b2 = d1.isBeforeNow(); 7. boolean b3 = d1.isEqualNow(); 8. 9. //和其餘日期比 10. boolean f1 = d1.isAfter(d2); 11. boolean f2 = d1.isBefore(d2); 12. boolean f3 = d1.isEqual(d2); 十、格式化輸出 1. DateTime dateTime = new DateTime(); 2. 3. String s1 = dateTime.toString("yyyy/MM/dd hh:mm:ss.SSSa"); 4. String s2 = dateTime.toString("yyyy-MM-dd HH:mm:ss"); 5. String s3 = dateTime.toString("EEEE dd MMMM, yyyy HH:mm:ssa"); 6. String s4 = dateTime.toString("yyyy/MM/dd HH:mm ZZZZ"); 7. String s5 = dateTime.toString("yyyy/MM/dd HH:mm Z");
案例:
1 public static DateTime getNowWeekMonday() { 2 DateTime date = DateTime.now(); 3 int dayOfWeek = date.getDayOfWeek(); 4 return DateTime.parse(date.minusDays(dayOfWeek - 1).toString("yyyy-MM-dd")); 5 }
1 private static final String DATE_FORMAT = "yyyy-MM-dd"; 2 3 //每週一0點0分0秒觸發,處理上上週 4 @Scheduled(cron = "0 0 0 ? * MON ") 5 public void weeklyRemind() { 6 logger.info("CyclePendingReminderTask.weeklyRemind"); 7 logger.info("週期性待處理提醒任務開始"); 8 String now = DateTime.now().toString(DATE_FORMAT); 9 //往前推2周,上上週週一 10 String from = DateTime.parse(now, ISODateTimeFormat.dateElementParser()) 11 .minusWeeks(2).toString(DATE_FORMAT); 12 //上上週週日 13 String to = DateTime.parse(from, ISODateTimeFormat.dateElementParser()) 14 .plusWeeks(1).minusDays(1).toString(DATE_FORMAT); 15 //上上週週一0點時間戳 16 long fromTime = DateTime.parse(from, ISODateTimeFormat.dateElementParser()).getMillis(); 17 //上週週一0點時間戳 18 long toTime = DateTime.parse(to, ISODateTimeFormat.dateElementParser()).plus(1).getMillis(); 19 List<String> userIdList = ideaService.getUserIdList(); 20 for (String userId : userIdList) { 21 List<Idea> ideaList = ideaService.findIdeasByCreateAt(userId, fromTime, toTime); 22 //有建立想法纔會有提醒 23 if (ideaList.size() > 0) { 24 CyclePendingIdeaReminder reminder = new CyclePendingIdeaReminder(); 25 reminder.setUserId(userId); 26 reminder.setFrom(from); 27 reminder.setTo(to); 28 reminder.setFinished(false); 29 cpiReminderService.save(reminder); 30 } 31 } 32 logger.info("週期性待處理提醒任務完成"); 33 }
1 //每個月一號0點0分0秒觸發 2 //當中再判斷當前月份進行季度和年度的處理操做 3 @Scheduled(cron = "0 0 0 1 * ? ") 4 public void monthlySelectionRemind() { 5 logger.info("IdeaSelectionReminderTask monthlySelectionRemind start."); 6 DateTime nowTime = DateTime.now(); 7 int month = nowTime.getMonthOfYear(); 8 String now = nowTime.toString(DATE_FORMAT); 9 //年度處理: 1 10 if (month == 1) { 11 logger.info("年度精選任務開始"); 12 String from = DateTime.parse(now, ISODateTimeFormat.dateElementParser()) 13 .minusYears(1).toString(DATE_FORMAT); 14 String to = DateTime.parse(now, ISODateTimeFormat.dateElementParser()) 15 .minusDays(1).toString(DATE_FORMAT); 16 doMonthly(from, to, OriginalityType.year); 17 logger.info("年度精選任務完成"); 18 } 19 //季度處理: 3(4) 6(7) 9(10) 12(1) 20 if (month == 4 || month == 7 || month == 10 || month == 1) { 21 logger.info("季度精選任務開始"); 22 String from = DateTime.parse(now, ISODateTimeFormat.dateElementParser()) 23 .minusMonths(3).toString(DATE_FORMAT); 24 String to = DateTime.parse(now, ISODateTimeFormat.dateElementParser()) 25 .minusDays(1).toString(DATE_FORMAT); 26 doMonthly(from, to, OriginalityType.quarter); 27 logger.info("季度精選任務完成"); 28 } 29 //月份處理 30 logger.info("月精選任務開始"); 31 String from = DateTime.parse(now, ISODateTimeFormat.dateElementParser()) 32 .minusMonths(1).toString(DATE_FORMAT); 33 String to = DateTime.parse(now, ISODateTimeFormat.dateElementParser()) 34 .minusDays(1).toString(DATE_FORMAT); 35 doMonthly(from, to, OriginalityType.month); 36 logger.info("月精選任務完成"); 37 logger.info("IdeaSelectionReminderTask monthlySelectionRemind finish."); 38 }
1 // 今日凌晨 2 Date date = DateTime.parse(DateTime.now().toString("yyyy-MM-dd")).toDate()
1 // 今天9點對應的日期 2 Date date = DateTime.parse(DateTime.now().toString("yyyy-MM-dd")).hourOfDay().addToCopy(9).toDate();
1 // 當前時間加1分鐘 2 Date date = DateTime.now().minuteOfHour().addToCopy(1)).toDate()