diff --git a/jdk/make/sun/javazic/tzdata/VERSION b/jdk/make/sun/javazic/tzdata/VERSION index 85db871ccf3..8ad1064e058 100644 --- a/jdk/make/sun/javazic/tzdata/VERSION +++ b/jdk/make/sun/javazic/tzdata/VERSION @@ -21,4 +21,4 @@ # or visit www.oracle.com if you need additional information or have any # questions. # -tzdata2012i +tzdata2013c diff --git a/jdk/make/sun/javazic/tzdata/africa b/jdk/make/sun/javazic/tzdata/africa index 7db9b3d269d..2f5d3c5e3fc 100644 --- a/jdk/make/sun/javazic/tzdata/africa +++ b/jdk/make/sun/javazic/tzdata/africa @@ -27,9 +27,9 @@ # This data is by no means authoritative; if you think you know better, # go ahead and edit the file (and please send any changes to -# tz@elsie.nci.nih.gov for general use in the future). +# tz@iana.org for general use in the future). -# From Paul Eggert (2006-03-22): +# From Paul Eggert (2013-02-21): # # A good source for time zone historical data outside the U.S. is # Thomas G. Shanks and Rique Pottenger, The International Atlas (6th edition), @@ -48,6 +48,10 @@ # Whitman Publishing Co, 2 Niagara Av, Ealing, London (undated), which # I found in the UCLA library. # +# For data circa 1899, a common source is: +# Milne J. Civil time. Geogr J. 1899 Feb;13(2):173-94 +# . +# # A reliable and entertaining source about time zones is # Derek Howse, Greenwich time and longitude, Philip Wilson Publishers (1997). # @@ -139,8 +143,12 @@ Zone Africa/Porto-Novo 0:10:28 - LMT 1912 1:00 - WAT # Botswana +# From Paul Eggert (2013-02-21): +# Milne says they were regulated by the Cape Town Signal in 1899; +# assume they switched to 2:00 when Cape Town did. # Zone NAME GMTOFF RULES FORMAT [UNTIL] Zone Africa/Gaborone 1:43:40 - LMT 1885 + 1:30 - SAST 1903 Mar 2:00 - CAT 1943 Sep 19 2:00 2:00 1:00 CAST 1944 Mar 19 2:00 2:00 - CAT @@ -212,6 +220,11 @@ Zone Africa/Djibouti 2:52:36 - LMT 1911 Jul # Egypt +# Milne says Cairo used 2:05:08.9, the local mean time of the Abbasizeh +# observatory; round to nearest. Milne also says that the official time for +# Egypt was mean noon at the Great Pyramid, 2:04:30.5, but apparently this +# did not apply to Cairo, Alexandria, or Port Said. + # Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S Rule Egypt 1940 only - Jul 15 0:00 1:00 S Rule Egypt 1940 only - Oct 1 0:00 0 - @@ -352,7 +365,7 @@ Rule Egypt 2010 only - Sep 10 0:00 1:00 S Rule Egypt 2010 only - Sep lastThu 23:00s 0 - # Zone NAME GMTOFF RULES FORMAT [UNTIL] -Zone Africa/Cairo 2:05:00 - LMT 1900 Oct +Zone Africa/Cairo 2:05:09 - LMT 1900 Oct 2:00 Egypt EE%sT # Equatorial Guinea @@ -447,6 +460,20 @@ Zone Africa/Monrovia -0:43:08 - LMT 1882 # Libya +# From Even Scharning (2012-11-10): +# Libya set their time one hour back at 02:00 on Saturday November 10. +# http://www.libyaherald.com/2012/11/04/clocks-to-go-back-an-hour-on-saturday/ +# Here is an official source [in Arabic]: http://ls.ly/fb6Yc +# +# Steffen Thorsen forwarded a translation (2012-11-10) in +# http://mm.icann.org/pipermail/tz/2012-November/018451.html +# +# From Tim Parenti (2012-11-11): +# Treat the 2012-11-10 change as a zone change from UTC+2 to UTC+1. +# The DST rules planned for 2013 and onward roughly mirror those of Europe +# (either two days before them or five days after them, so as to fall on +# lastFri instead of lastSun). + # Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S Rule Libya 1951 only - Oct 14 2:00 1:00 S Rule Libya 1952 only - Jan 1 0:00 0 - @@ -461,17 +488,21 @@ Rule Libya 1986 only - Apr 4 0:00 1:00 S Rule Libya 1986 only - Oct 3 0:00 0 - Rule Libya 1987 1989 - Apr 1 0:00 1:00 S Rule Libya 1987 1989 - Oct 1 0:00 0 - +Rule Libya 1997 only - Apr 4 0:00 1:00 S +Rule Libya 1997 only - Oct 4 0:00 0 - +Rule Libya 2013 max - Mar lastFri 1:00 1:00 S +Rule Libya 2013 max - Oct lastFri 2:00 0 - # Zone NAME GMTOFF RULES FORMAT [UNTIL] Zone Africa/Tripoli 0:52:44 - LMT 1920 1:00 Libya CE%sT 1959 2:00 - EET 1982 1:00 Libya CE%sT 1990 May 4 -# The following entries are from Shanks & Pottenger; +# The 1996 and 1997 entries are from Shanks & Pottenger; # the IATA SSIM data contain some obvious errors. 2:00 - EET 1996 Sep 30 - 1:00 - CET 1997 Apr 4 - 1:00 1:00 CEST 1997 Oct 4 - 2:00 - EET + 1:00 Libya CE%sT 1997 Oct 4 + 2:00 - EET 2012 Nov 10 2:00 + 1:00 Libya CE%sT # Madagascar # Zone NAME GMTOFF RULES FORMAT [UNTIL] @@ -838,6 +869,41 @@ Zone Indian/Mayotte 3:00:56 - LMT 1911 Jul # Mamoutzou # 3:00 am Friday, July 20, 2012 and will again be advanced by 60 minutes # August 20, 2012 from 2:00 am. +# From Paul Eggert (2013-03-06): +# Morocco's daylight-saving transitions due to Ramadan seem to be +# announced a bit in advance. On 2012-07-11 the Moroccan government +# announced that year's Ramadan daylight-saving transitions would be +# 2012-07-20 and 2012-08-20; see +# . +# +# To estimate what the Moroccan government will do in future years, +# transition dates for 2013 through 2021 were determined by running +# the following program under GNU Emacs 24.3: +# +# (let ((islamic-year 1434)) +# (while (< islamic-year 1444) +# (let ((a +# (calendar-gregorian-from-absolute +# (calendar-islamic-to-absolute (list 9 1 islamic-year)))) +# (b +# (calendar-gregorian-from-absolute +# (calendar-islamic-to-absolute (list 10 1 islamic-year))))) +# (insert +# (format +# (concat "Rule\tMorocco\t%d\tonly\t-\t%s\t %2d\t 3:00\t0\t-\n" +# "Rule\tMorocco\t%d\tonly\t-\t%s\t %2d\t 2:00\t1:00\tS\n") +# (car (cdr (cdr a))) (calendar-month-name (car a) t) (car (cdr a)) +# (car (cdr (cdr b))) (calendar-month-name (car b) t) (car (cdr b))))) +# (setq islamic-year (+ 1 islamic-year)))) +# +# with the results hand-edited for 2020-2022, when the normal spring-forward +# date falls during the estimated Ramadan. +# +# From 2023 through 2038 Ramadan is not predicted to overlap with +# daylight saving time. Starting in 2039 there will be overlap again, +# but 32-bit time_t values roll around in 2038 so for now do not worry +# about dates after 2038. + # RULE NAME FROM TO TYPE IN ON AT SAVE LETTER/S Rule Morocco 1939 only - Sep 12 0:00 1:00 S @@ -863,10 +929,28 @@ Rule Morocco 2010 only - May 2 0:00 1:00 S Rule Morocco 2010 only - Aug 8 0:00 0 - Rule Morocco 2011 only - Apr 3 0:00 1:00 S Rule Morocco 2011 only - Jul 31 0 0 - -Rule Morocco 2012 max - Apr lastSun 2:00 1:00 S +Rule Morocco 2012 2019 - Apr lastSun 2:00 1:00 S Rule Morocco 2012 max - Sep lastSun 3:00 0 - Rule Morocco 2012 only - Jul 20 3:00 0 - Rule Morocco 2012 only - Aug 20 2:00 1:00 S +Rule Morocco 2013 only - Jul 9 3:00 0 - +Rule Morocco 2013 only - Aug 8 2:00 1:00 S +Rule Morocco 2014 only - Jun 29 3:00 0 - +Rule Morocco 2014 only - Jul 29 2:00 1:00 S +Rule Morocco 2015 only - Jun 18 3:00 0 - +Rule Morocco 2015 only - Jul 18 2:00 1:00 S +Rule Morocco 2016 only - Jun 7 3:00 0 - +Rule Morocco 2016 only - Jul 7 2:00 1:00 S +Rule Morocco 2017 only - May 27 3:00 0 - +Rule Morocco 2017 only - Jun 26 2:00 1:00 S +Rule Morocco 2018 only - May 16 3:00 0 - +Rule Morocco 2018 only - Jun 15 2:00 1:00 S +Rule Morocco 2019 only - May 6 3:00 0 - +Rule Morocco 2019 only - Jun 5 2:00 1:00 S +Rule Morocco 2020 only - May 24 2:00 1:00 S +Rule Morocco 2021 only - May 13 2:00 1:00 S +Rule Morocco 2022 only - May 3 2:00 1:00 S +Rule Morocco 2023 max - Apr lastSun 2:00 1:00 S # Zone NAME GMTOFF RULES FORMAT [UNTIL] Zone Africa/Casablanca -0:30:20 - LMT 1913 Oct 26 diff --git a/jdk/make/sun/javazic/tzdata/antarctica b/jdk/make/sun/javazic/tzdata/antarctica index 64b71d5c052..daa03ea830c 100644 --- a/jdk/make/sun/javazic/tzdata/antarctica +++ b/jdk/make/sun/javazic/tzdata/antarctica @@ -73,38 +73,8 @@ Rule ChileAQ 2009 only - Mar Sun>=9 3:00u 0 - Rule ChileAQ 2010 only - Apr Sun>=1 3:00u 0 - Rule ChileAQ 2011 only - May Sun>=2 3:00u 0 - Rule ChileAQ 2011 only - Aug Sun>=16 4:00u 1:00 S -Rule ChileAQ 2012 only - Apr Sun>=23 3:00u 0 - -Rule ChileAQ 2012 only - Sep Sun>=2 4:00u 1:00 S -Rule ChileAQ 2013 max - Mar Sun>=9 3:00u 0 - -Rule ChileAQ 2013 max - Oct Sun>=9 4:00u 1:00 S - -# These rules are stolen from the `australasia' file. -Rule AusAQ 1917 only - Jan 1 0:01 1:00 - -Rule AusAQ 1917 only - Mar 25 2:00 0 - -Rule AusAQ 1942 only - Jan 1 2:00 1:00 - -Rule AusAQ 1942 only - Mar 29 2:00 0 - -Rule AusAQ 1942 only - Sep 27 2:00 1:00 - -Rule AusAQ 1943 1944 - Mar lastSun 2:00 0 - -Rule AusAQ 1943 only - Oct 3 2:00 1:00 - -Rule ATAQ 1967 only - Oct Sun>=1 2:00s 1:00 - -Rule ATAQ 1968 only - Mar lastSun 2:00s 0 - -Rule ATAQ 1968 1985 - Oct lastSun 2:00s 1:00 - -Rule ATAQ 1969 1971 - Mar Sun>=8 2:00s 0 - -Rule ATAQ 1972 only - Feb lastSun 2:00s 0 - -Rule ATAQ 1973 1981 - Mar Sun>=1 2:00s 0 - -Rule ATAQ 1982 1983 - Mar lastSun 2:00s 0 - -Rule ATAQ 1984 1986 - Mar Sun>=1 2:00s 0 - -Rule ATAQ 1986 only - Oct Sun>=15 2:00s 1:00 - -Rule ATAQ 1987 1990 - Mar Sun>=15 2:00s 0 - -Rule ATAQ 1987 only - Oct Sun>=22 2:00s 1:00 - -Rule ATAQ 1988 1990 - Oct lastSun 2:00s 1:00 - -Rule ATAQ 1991 1999 - Oct Sun>=1 2:00s 1:00 - -Rule ATAQ 1991 2005 - Mar lastSun 2:00s 0 - -Rule ATAQ 2000 only - Aug lastSun 2:00s 1:00 - -Rule ATAQ 2001 max - Oct Sun>=1 2:00s 1:00 - -Rule ATAQ 2006 only - Apr Sun>=1 2:00s 0 - -Rule ATAQ 2007 only - Mar lastSun 2:00s 0 - -Rule ATAQ 2008 max - Apr Sun>=1 2:00s 0 - +Rule ChileAQ 2012 max - Apr Sun>=23 3:00u 0 - +Rule ChileAQ 2012 max - Sep Sun>=2 4:00u 1:00 S # Argentina - year-round bases # Belgrano II, Confin Coast, -770227-0343737, since 1972-02-05 @@ -147,10 +117,7 @@ Rule ATAQ 2008 max - Apr Sun>=1 2:00s 0 - # # From Steffen Thorsen (2010-03-10): -# We got these changes from the Australian Antarctic Division: -# - Macquarie Island will stay on UTC+11 for winter and therefore not -# switch back from daylight savings time when other parts of Australia do -# on 4 April. +# We got these changes from the Australian Antarctic Division: ... # # - Casey station reverted to its normal time of UTC+8 on 5 March 2010. # The change to UTC+11 is being considered as a regular summer thing but @@ -161,9 +128,6 @@ Rule ATAQ 2008 max - Apr Sun>=1 2:00s 0 - # # - Mawson station stays on UTC+5. # -# In addition to the Rule changes for Casey/Davis, it means that Macquarie -# will no longer be like Hobart and will have to have its own Zone created. -# # Background: # # http://www.timeanddate.com/news/time/antartica-time-changes-2010.html @@ -190,12 +154,6 @@ Zone Antarctica/Mawson 0 - zzz 1954 Feb 13 6:00 - MAWT 2009 Oct 18 2:00 # Mawson Time 5:00 - MAWT -Zone Antarctica/Macquarie 0 - zzz 1911 - 10:00 - EST 1916 Oct 1 2:00 - 10:00 1:00 EST 1917 Feb - 10:00 AusAQ EST 1967 - 10:00 ATAQ EST 2010 Apr 4 3:00 - 11:00 - MIST # Macquarie Island Time # References: # # Casey Weather (1998-02-26) diff --git a/jdk/make/sun/javazic/tzdata/asia b/jdk/make/sun/javazic/tzdata/asia index 9ef3ef8df54..7818c029a60 100644 --- a/jdk/make/sun/javazic/tzdata/asia +++ b/jdk/make/sun/javazic/tzdata/asia @@ -27,9 +27,9 @@ # This data is by no means authoritative; if you think you know better, # go ahead and edit the file (and please send any changes to -# tz@elsie.nci.nih.gov for general use in the future). +# tz@iana.org for general use in the future). -# From Paul Eggert (2006-03-22): +# From Paul Eggert (2013-02-21): # # A good source for time zone historical data outside the U.S. is # Thomas G. Shanks and Rique Pottenger, The International Atlas (6th edition), @@ -48,6 +48,10 @@ # Whitman Publishing Co, 2 Niagara Av, Ealing, London (undated), which # I found in the UCLA library. # +# For data circa 1899, a common source is: +# Milne J. Civil time. Geogr J. 1899 Feb;13(2):173-94 +# . +# # A reliable and entertaining source about time zones is # Derek Howse, Greenwich time and longitude, Philip Wilson Publishers (1997). # @@ -302,9 +306,12 @@ Zone Asia/Brunei 7:39:40 - LMT 1926 Mar # Bandar Seri Begawan 8:00 - BNT # Burma / Myanmar + +# Milne says 6:24:40 was the meridian of the time ball observatory at Rangoon. + # Zone NAME GMTOFF RULES FORMAT [UNTIL] Zone Asia/Rangoon 6:24:40 - LMT 1880 # or Yangon - 6:24:36 - RMT 1920 # Rangoon Mean Time? + 6:24:40 - RMT 1920 # Rangoon Mean Time? 6:30 - BURT 1942 May # Burma Time 9:00 - JST 1945 May 3 6:30 - MMT # Myanmar Time @@ -407,7 +414,8 @@ Zone Asia/Harbin 8:26:44 - LMT 1928 # or Haerbin 8:00 PRC C%sT # Zhongyuan Time ("Central plain Time") # most of China -Zone Asia/Shanghai 8:05:52 - LMT 1928 +# Milne gives 8:05:56.7; round to nearest. +Zone Asia/Shanghai 8:05:57 - LMT 1928 8:00 Shang C%sT 1949 8:00 PRC C%sT # Long-shu Time (probably due to Long and Shu being two names of that area) @@ -504,6 +512,10 @@ Zone Asia/Kashgar 5:03:56 - LMT 1928 # or Kashi or Kaxgar 8:00 PRC C%sT +# Hong Kong (Xianggang) + +# Milne gives 7:36:41.7; round this. + # From Lee Yiu Chung (2009-10-24): # I found there are some mistakes for the...DST rule for Hong # Kong. [According] to the DST record from Hong Kong Observatory (actually, @@ -570,7 +582,6 @@ Zone Asia/Kashgar 5:03:56 - LMT 1928 # or Kashi or Kaxgar # The Japanese surrender of Hong Kong was signed 1945-09-15. # For lack of anything better, use start of those days as the transition times. -# Hong Kong (Xianggang) # Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S Rule HK 1941 only - Apr 1 3:30 1:00 S Rule HK 1941 only - Sep 30 3:30 0 - @@ -592,7 +603,7 @@ Rule HK 1973 only - Dec 30 3:30 1:00 S Rule HK 1979 only - May Sun>=8 3:30 1:00 S Rule HK 1979 only - Oct Sun>=16 3:30 0 - # Zone NAME GMTOFF RULES FORMAT [UNTIL] -Zone Asia/Hong_Kong 7:36:36 - LMT 1904 Oct 30 +Zone Asia/Hong_Kong 7:36:42 - LMT 1904 Oct 30 8:00 HK HK%sT 1941 Dec 25 9:00 - JST 1945 Sep 15 8:00 HK HK%sT @@ -669,6 +680,9 @@ Zone Asia/Macau 7:34:20 - LMT 1912 ############################################################################### # Cyprus +# +# Milne says the Eastern Telegraph Company used 2:14:00. Stick with LMT. +# # Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S Rule Cyprus 1975 only - Apr 13 0:00 1:00 S Rule Cyprus 1975 only - Oct 12 0:00 0 - @@ -1222,7 +1236,6 @@ Rule Zion 2012 only - Mar Fri>=26 2:00 1:00 D Rule Zion 2012 only - Sep 23 2:00 0 S # From Ephraim Silverberg (2012-10-18): - # Yesterday, the Interior Ministry Committee, after more than a year # past, approved sending the proposed June 2011 changes to the Time # Decree Law back to the Knesset for second and third (final) votes @@ -1235,6 +1248,10 @@ Rule Zion 2012 only - Sep 23 2:00 0 S # later (i.e. at 02:00 the first Monday after October 2). # [Rosh Hashana holidays are factored in until 2100.] +# From Ephraim Silverberg (2012-11-05): +# The Knesset passed today (in second and final readings) the amendment to the +# Time Decree Law making the changes ... law. + # Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S Rule Zion 2013 max - Mar Fri>=23 2:00 1:00 D Rule Zion 2013 2026 - Oct Sun>=2 2:00 0 S @@ -1824,8 +1841,11 @@ Zone Asia/Kathmandu 5:41:16 - LMT 1920 5:45 - NPT # Nepal Time # Oman + +# Milne says 3:54:24 was the meridian of the Muscat Tidal Observatory. + # Zone NAME GMTOFF RULES FORMAT [UNTIL] -Zone Asia/Muscat 3:54:20 - LMT 1920 +Zone Asia/Muscat 3:54:24 - LMT 1920 4:00 - GST # Pakistan @@ -2072,8 +2092,7 @@ Zone Asia/Karachi 4:28:12 - LMT 1907 # occurred before our cutoff date of 1970. # However, as we get more information, we may need to add entries # for parts of the West Bank as they transitioned from Israel's rules -# to Palestine's rules. If you have more info about this, please -# send it to tz@elsie.nci.nih.gov for incorporation into future editions. +# to Palestine's rules. # From IINS News Service - Israel - 1998-03-23 10:38:07 Israel time, # forwarded by Ephraim Silverberg: @@ -2295,11 +2314,20 @@ Zone Asia/Karachi 4:28:12 - LMT 1907 # http://www.timeanddate.com/news/time/gaza-west-bank-dst-2012.html # -# From Arthur David Olson (2012-03-27): -# The timeanddate article for 2012 says that "the end date has not yet been -# announced" and that "Last year, both...paused daylight saving time during... -# Ramadan. It is not yet known [for] 2012." -# For now, assume both switch back on the last Friday in September. XXX +# From Steffen Thorsen (2013-03-26): +# The following news sources tells that Palestine will "start daylight saving +# time from midnight on Friday, March 29, 2013" (translated). +# [These are in Arabic and are for Gaza and for Ramallah, respectively.] +# http://www.samanews.com/index.php?act=Show&id=154120 +# http://safa.ps/details/news/99844/%D8%B1%D8%A7%D9%85-%D8%A7%D9%84%D9%84%D9%87-%D8%A8%D8%AF%D8%A1-%D8%A7%D9%84%D8%AA%D9%88%D9%82%D9%8A%D8%AA-%D8%A7%D9%84%D8%B5%D9%8A%D9%81%D9%8A-29-%D8%A7%D9%84%D8%AC%D8%A7%D8%B1%D9%8A.html + +# From Paul Eggert (2013-04-15): +# For future dates, guess the last Thursday in March at 24:00 through +# the first Friday on or after September 21 at 01:00. This is consistent with +# the predictions in today's editions of the following URLs, +# which are for Gaza and Hebron respectively: +# http://www.timeanddate.com/worldclock/timezone.html?n=702 +# http://www.timeanddate.com/worldclock/timezone.html?n=2364 # Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S Rule EgyptAsia 1957 only - May 10 0:00 1:00 S @@ -2313,19 +2341,20 @@ Rule Palestine 1999 2005 - Apr Fri>=15 0:00 1:00 S Rule Palestine 1999 2003 - Oct Fri>=15 0:00 0 - Rule Palestine 2004 only - Oct 1 1:00 0 - Rule Palestine 2005 only - Oct 4 2:00 0 - -Rule Palestine 2006 2008 - Apr 1 0:00 1:00 S +Rule Palestine 2006 2007 - Apr 1 0:00 1:00 S Rule Palestine 2006 only - Sep 22 0:00 0 - Rule Palestine 2007 only - Sep Thu>=8 2:00 0 - -Rule Palestine 2008 only - Aug lastFri 0:00 0 - -Rule Palestine 2009 only - Mar lastFri 0:00 1:00 S -Rule Palestine 2009 only - Sep Fri>=1 2:00 0 - -Rule Palestine 2010 only - Mar lastSat 0:01 1:00 S +Rule Palestine 2008 2009 - Mar lastFri 0:00 1:00 S +Rule Palestine 2008 only - Sep 1 0:00 0 - +Rule Palestine 2009 only - Sep Fri>=1 1:00 0 - +Rule Palestine 2010 only - Mar 26 0:00 1:00 S Rule Palestine 2010 only - Aug 11 0:00 0 - - -# From Arthur David Olson (2011-09-20): -# 2011 transitions per http://www.timeanddate.com as of 2011-09-20. -# From Paul Eggert (2012-10-12): -# 2012 transitions per http://www.timeanddate.com as of 2012-10-12. +Rule Palestine 2011 only - Apr 1 0:01 1:00 S +Rule Palestine 2011 only - Aug 1 0:00 0 - +Rule Palestine 2011 only - Aug 30 0:00 1:00 S +Rule Palestine 2011 only - Sep 30 0:00 0 - +Rule Palestine 2012 max - Mar lastThu 24:00 1:00 S +Rule Palestine 2012 max - Sep Fri>=21 1:00 0 - # Zone NAME GMTOFF RULES FORMAT [UNTIL] Zone Asia/Gaza 2:17:52 - LMT 1900 Oct @@ -2333,26 +2362,20 @@ Zone Asia/Gaza 2:17:52 - LMT 1900 Oct 2:00 EgyptAsia EE%sT 1967 Jun 5 2:00 Zion I%sT 1996 2:00 Jordan EE%sT 1999 - 2:00 Palestine EE%sT 2011 Apr 2 12:01 - 2:00 1:00 EEST 2011 Aug 1 - 2:00 - EET 2012 Mar 30 - 2:00 1:00 EEST 2012 Sep 21 1:00 - 2:00 - EET + 2:00 Palestine EE%sT 2008 Aug 29 0:00 + 2:00 - EET 2008 Sep + 2:00 Palestine EE%sT 2010 + 2:00 - EET 2010 Mar 27 0:01 + 2:00 Palestine EE%sT 2011 Aug 1 + 2:00 - EET 2012 + 2:00 Palestine EE%sT Zone Asia/Hebron 2:20:23 - LMT 1900 Oct 2:00 Zion EET 1948 May 15 2:00 EgyptAsia EE%sT 1967 Jun 5 2:00 Zion I%sT 1996 2:00 Jordan EE%sT 1999 - 2:00 Palestine EE%sT 2008 Aug - 2:00 1:00 EEST 2008 Sep - 2:00 Palestine EE%sT 2011 Apr 1 12:01 - 2:00 1:00 EEST 2011 Aug 1 - 2:00 - EET 2011 Aug 30 - 2:00 1:00 EEST 2011 Sep 30 3:00 - 2:00 - EET 2012 Mar 30 - 2:00 1:00 EEST 2012 Sep 21 1:00 - 2:00 - EET + 2:00 Palestine EE%sT # Paracel Is # no information @@ -2421,6 +2444,13 @@ Zone Asia/Singapore 6:55:25 - LMT 1901 Jan 1 # no information # Sri Lanka + +# From Paul Eggert (2013-02-21): +# Milne says "Madras mean time use from May 1, 1898. Prior to this Colombo +# mean time, 5h. 4m. 21.9s. F., was used." But 5:04:21.9 differs considerably +# from Colombo's meridian 5:19:24, so for now ignore Milne and stick with +# Shanks and Pottenger. + # From Paul Eggert (1996-09-03): # "Sri Lanka advances clock by an hour to avoid blackout" # (www.virtual-pc.com/lankaweb/news/items/240596-2.html, 1996-05-24, @@ -2720,6 +2750,12 @@ Zone Asia/Tashkent 4:37:12 - LMT 1924 May 2 # Vietnam +# From Paul Eggert (2013-02-21): +# Milne gives 7:16:56 for the meridian of Saigon in 1899, as being +# used in Lower Laos, Cambodia, and Annam. But this is quite a ways +# from Saigon's location. For now, ignore this and stick with Shanks +# and Pottenger. + # From Arthur David Olson (2008-03-18): # The English-language name of Vietnam's most populous city is "Ho Chi Min City"; # we use Ho_Chi_Minh below to avoid a name of more than 14 characters. @@ -2733,6 +2769,10 @@ Zone Asia/Ho_Chi_Minh 7:06:40 - LMT 1906 Jun 9 7:00 - ICT # Yemen + +# Milne says 2:59:54 was the meridian of the saluting battery at Aden, +# and that Yemen was at 1:55:56, the meridian of the Hagia Sophia. + # Zone NAME GMTOFF RULES FORMAT [UNTIL] -Zone Asia/Aden 3:00:48 - LMT 1950 +Zone Asia/Aden 2:59:54 - LMT 1950 3:00 - AST diff --git a/jdk/make/sun/javazic/tzdata/australasia b/jdk/make/sun/javazic/tzdata/australasia index 7f83448f3fb..db954a81dcd 100644 --- a/jdk/make/sun/javazic/tzdata/australasia +++ b/jdk/make/sun/javazic/tzdata/australasia @@ -241,9 +241,26 @@ Zone Australia/Lord_Howe 10:36:20 - LMT 1895 Feb # no times are set # # Macquarie -# permanent occupation (scientific station) since 1948; -# sealing and penguin oil station operated 1888/1917 -# like Australia/Hobart +# Permanent occupation (scientific station) 1911-1915 and since 25 March 1948; +# sealing and penguin oil station operated Nov 1899 to Apr 1919. See the +# Tasmania Parks & Wildlife Service history of sealing at Macquarie Island +# +# . +# Guess that it was like Australia/Hobart while inhabited before 2010. +# +# From Steffen Thorsen (2010-03-10): +# We got these changes from the Australian Antarctic Division: +# - Macquarie Island will stay on UTC+11 for winter and therefore not +# switch back from daylight savings time when other parts of Australia do +# on 4 April. +Zone Antarctica/Macquarie 0 - zzz 1899 Nov + 10:00 - EST 1916 Oct 1 2:00 + 10:00 1:00 EST 1917 Feb + 10:00 Aus EST 1919 Apr + 0 - zzz 1948 Mar 25 + 10:00 Aus EST 1967 + 10:00 AT EST 2010 Apr 4 3:00 + 11:00 - MIST # Macquarie I Standard Time # Christmas # Zone NAME GMTOFF RULES FORMAT [UNTIL] @@ -269,6 +286,9 @@ Zone Indian/Cocos 6:27:40 - LMT 1900 6:30 - CCT # Cocos Islands Time # Fiji + +# Milne gives 11:55:44 for Suva. + # From Alexander Krivenyshev (2009-11-10): # According to Fiji Broadcasting Corporation, Fiji plans to re-introduce DST # from November 29th 2009 to April 25th 2010. @@ -362,7 +382,7 @@ Rule Fiji 2010 max - Oct Sun>=18 2:00 1:00 S Rule Fiji 2011 only - Mar Sun>=1 3:00 0 - Rule Fiji 2012 max - Jan Sun>=18 3:00 0 - # Zone NAME GMTOFF RULES FORMAT [UNTIL] -Zone Pacific/Fiji 11:53:40 - LMT 1915 Oct 26 # Suva +Zone Pacific/Fiji 11:55:44 - LMT 1915 Oct 26 # Suva 12:00 Fiji FJ%sT # Fiji Time # French Polynesia @@ -803,9 +823,9 @@ Zone Pacific/Wallis 12:15:20 - LMT 1901 # This data is by no means authoritative; if you think you know better, # go ahead and edit the file (and please send any changes to -# tz@elsie.nci.nih.gov for general use in the future). +# tz@iana.org for general use in the future). -# From Paul Eggert (2006-03-22): +# From Paul Eggert (2013-02-21): # A good source for time zone historical data outside the U.S. is # Thomas G. Shanks and Rique Pottenger, The International Atlas (6th edition), # San Diego: ACS Publications, Inc. (2003). @@ -823,6 +843,10 @@ Zone Pacific/Wallis 12:15:20 - LMT 1901 # Whitman Publishing Co, 2 Niagara Av, Ealing, London (undated), which # I found in the UCLA library. # +# For data circa 1899, a common source is: +# Milne J. Civil time. Geogr J. 1899 Feb;13(2):173-94 +# . +# # A reliable and entertaining source about time zones is # Derek Howse, Greenwich time and longitude, Philip Wilson Publishers (1997). # diff --git a/jdk/make/sun/javazic/tzdata/europe b/jdk/make/sun/javazic/tzdata/europe index 9a0d0b9db94..268504d0983 100644 --- a/jdk/make/sun/javazic/tzdata/europe +++ b/jdk/make/sun/javazic/tzdata/europe @@ -27,7 +27,7 @@ # This data is by no means authoritative; if you think you know better, # go ahead and edit the file (and please send any changes to -# tz@elsie.nci.nih.gov for general use in the future). +# tz@iana.org for general use in the future). # From Paul Eggert (2006-03-22): # A good source for time zone historical data outside the U.S. is @@ -53,6 +53,12 @@ # William Willett, The Waste of Daylight, 19th edition # (1914-03) # +# Milne J. Civil time. Geogr J. 1899 Feb;13(2):173-94 +# . He writes: +# "It is requested that corrections and additions to these tables +# may be sent to Mr. John Milne, Royal Geographical Society, +# Savile Row, London." Nowadays please email them to tz@iana.org. +# # Brazil's Departamento Servico da Hora (DSH), # # History of Summer Time @@ -689,6 +695,8 @@ Zone Europe/Andorra 0:06:04 - LMT 1901 # Austria +# Milne says Vienna time was 1:05:21. + # From Paul Eggert (2006-03-22): Shanks & Pottenger give 1918-06-16 and # 1945-11-18, but the Austrian Federal Office of Metrology and # Surveying (BEV) gives 1918-09-16 and for Vienna gives the "alleged" @@ -706,7 +714,7 @@ Rule Austria 1948 only - Apr 18 2:00s 1:00 S Rule Austria 1980 only - Apr 6 0:00 1:00 S Rule Austria 1980 only - Sep 28 0:00 0 - # Zone NAME GMTOFF RULES FORMAT [UNTIL] -Zone Europe/Vienna 1:05:20 - LMT 1893 Apr +Zone Europe/Vienna 1:05:21 - LMT 1893 Apr 1:00 C-Eur CE%sT 1920 1:00 Austria CE%sT 1940 Apr 1 2:00s 1:00 C-Eur CE%sT 1945 Apr 2 2:00s @@ -1262,6 +1270,21 @@ Zone Europe/Berlin 0:53:28 - LMT 1893 Apr 1:00 Germany CE%sT 1980 1:00 EU CE%sT +# From Tobias Conradi (2011-09-12): +# Busingen , surrounded by the Swiss canton +# Schaffhausen, did not start observing DST in 1980 as the rest of DE +# (West Germany at that time) and DD (East Germany at that time) did. +# DD merged into DE, the area is currently covered by code DE in ISO 3166-1, +# which in turn is covered by the zone Europe/Berlin. +# +# Source for the time in Busingen 1980: +# http://www.srf.ch/player/video?id=c012c029-03b7-4c2b-9164-aa5902cd58d3 + +# From Arthur David Olson (2012-03-03): +# Busingen and Zurich have shared clocks since 1970. + +Link Europe/Zurich Europe/Busingen + # Georgia # Please see the "asia" file for Asia/Tbilisi. # Herodotus (Histories, IV.45) says Georgia north of the Phasis (now Rioni) @@ -2066,6 +2089,70 @@ Zone Europe/Bucharest 1:44:24 - LMT 1891 Oct # Russia +# From Alexander Krivenyshev (2011-09-15): +# Based on last Russian Government Decree # 725 on August 31, 2011 +# (Government document +# +# http://www.government.ru/gov/results/16355/print/ +# +# in Russian) +# there are few corrections have to be made for some Russian time zones... +# All updated Russian Time Zones were placed in table and translated to English +# by WorldTimeZone.com at the link below: +# +# http://www.worldtimezone.com/dst_news/dst_news_russia36.htm +# + +# From Sanjeev Gupta (2011-09-27): +# Scans of [Decree #23 of January 8, 1992] are available at: +# +# http://government.consultant.ru/page.aspx?1223966 +# They are in Cyrillic letters (presumably Russian). + +# From Arthur David Olson (2012-05-09): +# Regarding the instant when clocks in time-zone-shifting parts of Russia +# changed in September 2011: +# +# One source is +# < a href="http://government.ru/gov/results/16355/> +# http://government.ru/gov/results/16355/ +# +# which, according to translate.google.com, begins "Decree of August 31, +# 2011 No 725" and contains no other dates or "effective date" information. +# +# Another source is +# +# http://www.rg.ru/2011/09/06/chas-zona-dok.html +# +# which, according to translate.google.com, begins "Resolution of the +# Government of the Russian Federation on August 31, 2011 N 725" and also +# contains "Date first official publication: September 6, 2011 Posted on: +# in the 'RG' - Federal Issue number 5573 September 6, 2011" but which +# does not contain any "effective date" information. +# +# Another source is +# +# http://en.wikipedia.org/wiki/Oymyakonsky_District#cite_note-RuTime-7 +# +# which, in note 8, contains "Resolution #725 of August 31, 2011... +# Effective as of after 7 days following the day of the official publication" +# but which does not contain any reference to September 6, 2011. +# +# The Wikipedia article refers to +# +# http://base.consultant.ru/cons/cgi/online.cgi?req=doc;base=LAW;n=118896 +# +# which seems to copy the text of the government.ru page. +# +# Tobias Conradi combines Wikipedia's +# "as of after 7 days following the day of the official publication" +# with www.rg.ru's "Date of first official publication: September 6, 2011" to get +# September 13, 2011 as the cutover date (unusually, a Tuesday, as Tobias Conradi notes). +# +# None of the sources indicates a time of day for changing clocks. +# +# Go with 2011-09-13 0:00s. + # From Paul Eggert (2006-03-22): # Except for Moscow after 1919-07-01, I invented the time zone abbreviations. # Moscow time zone abbreviations after 1919-07-01, and Moscow rules after 1991, @@ -2293,14 +2380,32 @@ Zone Asia/Yakutsk 8:38:40 - LMT 1919 Dec 15 # [parts of] Respublika Sakha (Yakutiya). # From Oscar van Vlijmen (2009-11-29): -# The Sakha districts are: Bulunskij, Verkhoyanskij, Tomponskij, Ust'-Majskij, -# Ust'-Yanskij. +# The Sakha districts are: Bulunskij, Verkhoyanskij, ... Ust'-Yanskij. Zone Asia/Vladivostok 8:47:44 - LMT 1922 Nov 15 9:00 - VLAT 1930 Jun 21 # Vladivostok Time 10:00 Russia VLA%sT 1991 Mar 31 2:00s 9:00 Russia VLA%sST 1992 Jan 19 2:00s 10:00 Russia VLA%sT 2011 Mar 27 2:00s 11:00 - VLAT + +# From Arthur David Olson (2012-05-09): +# Tomponskij and Ust'-Majskij switched from Vladivostok time to Yakutsk time +# in 2011. +# +# From Paul Eggert (2012-11-25): +# Shanks and Pottenger (2003) has Khandyga on Yakutsk time. +# Make a wild guess that it switched to Vladivostok time in 2004. +# This transition is no doubt wrong, but we have no better info. +# +Zone Asia/Khandyga 9:02:13 - LMT 1919 Dec 15 + 8:00 - YAKT 1930 Jun 21 # Yakutsk Time + 9:00 Russia YAK%sT 1991 Mar 31 2:00s + 8:00 Russia YAK%sT 1992 Jan 19 2:00s + 9:00 Russia YAK%sT 2004 + 10:00 Russia VLA%sT 2011 Mar 27 2:00s + 11:00 - VLAT 2011 Sep 13 0:00s # Decree 725? + 10:00 - YAKT + # # Sakhalinskaya oblast'. # The Zone name should be Yuzhno-Sakhalinsk, but that's too long. @@ -2319,14 +2424,26 @@ Zone Asia/Sakhalin 9:30:48 - LMT 1905 Aug 23 # From Oscar van Vlijmen (2009-11-29): # The Sakha districts are: Abyjskij, Allaikhovskij, Verkhhhnekolymskij, Momskij, -# Nizhnekolymskij, Ojmyakonskij, Srednekolymskij. +# Nizhnekolymskij, ... Srednekolymskij. Zone Asia/Magadan 10:03:12 - LMT 1924 May 2 10:00 - MAGT 1930 Jun 21 # Magadan Time 11:00 Russia MAG%sT 1991 Mar 31 2:00s 10:00 Russia MAG%sT 1992 Jan 19 2:00s 11:00 Russia MAG%sT 2011 Mar 27 2:00s 12:00 - MAGT -# + +# From Arthur David Olson (2012-05-09): +# Ojmyakonskij and the Kuril Islands switched from +# Magadan time to Vladivostok time in 2011. +Zone Asia/Ust-Nera 9:32:54 - LMT 1919 Dec 15 + 8:00 - YAKT 1930 Jun 21 # Yakutsk Time + 9:00 Russia YAKT 1981 Apr 1 + 11:00 Russia MAG%sT 1991 Mar 31 2:00s + 10:00 Russia MAG%sT 1992 Jan 19 2:00s + 11:00 Russia MAG%sT 2011 Mar 27 2:00s + 12:00 - MAGT 2011 Sep 13 0:00s # Decree 725? + 11:00 - VLAT + # From Oscar van Vlijmen (2001-08-25): [This region consists of] # Kamchatskaya oblast', Koryakskij avtonomnyj okrug. # diff --git a/jdk/make/sun/javazic/tzdata/northamerica b/jdk/make/sun/javazic/tzdata/northamerica index c3033267404..858bf811ac9 100644 --- a/jdk/make/sun/javazic/tzdata/northamerica +++ b/jdk/make/sun/javazic/tzdata/northamerica @@ -29,7 +29,7 @@ # This data is by no means authoritative; if you think you know better, # go ahead and edit the file (and please send any changes to -# tz@elsie.nci.nih.gov for general use in the future). +# tz@iana.org for general use in the future). # From Paul Eggert (1999-03-22): # A reliable and entertaining source about time zones is @@ -1042,6 +1042,9 @@ Zone America/Menominee -5:50:27 - LMT 1885 Sep 18 12:00 # William Willett, The Waste of Daylight, 19th edition # (1914-03) # +# Milne J. Civil time. Geogr J. 1899 Feb;13(2):173-94 +# . +# # See the `europe' file for Greenland. # Canada @@ -2577,6 +2580,8 @@ Zone America/Antigua -4:07:12 - LMT 1912 Mar 2 # Bahamas # +# For 1899 Milne gives -5:09:29.5; round that. +# # From Sue Williams (2006-12-07): # The Bahamas announced about a month ago that they plan to change their DST # rules to sync with the U.S. starting in 2007.... @@ -2586,11 +2591,14 @@ Zone America/Antigua -4:07:12 - LMT 1912 Mar 2 Rule Bahamas 1964 1975 - Oct lastSun 2:00 0 S Rule Bahamas 1964 1975 - Apr lastSun 2:00 1:00 D # Zone NAME GMTOFF RULES FORMAT [UNTIL] -Zone America/Nassau -5:09:24 - LMT 1912 Mar 2 +Zone America/Nassau -5:09:30 - LMT 1912 Mar 2 -5:00 Bahamas E%sT 1976 -5:00 US E%sT # Barbados + +# For 1899 Milne gives -3:58:29.2; round that. + # Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S Rule Barb 1977 only - Jun 12 2:00 1:00 D Rule Barb 1977 1978 - Oct Sun>=1 2:00 0 S @@ -2598,8 +2606,8 @@ Rule Barb 1978 1980 - Apr Sun>=15 2:00 1:00 D Rule Barb 1979 only - Sep 30 2:00 0 S Rule Barb 1980 only - Sep 25 2:00 0 S # Zone NAME GMTOFF RULES FORMAT [UNTIL] -Zone America/Barbados -3:58:28 - LMT 1924 # Bridgetown - -3:58:28 - BMT 1932 # Bridgetown Mean Time +Zone America/Barbados -3:58:29 - LMT 1924 # Bridgetown + -3:58:29 - BMT 1932 # Bridgetown Mean Time -4:00 Barb A%sT # Belize @@ -2617,6 +2625,9 @@ Zone America/Belize -5:52:48 - LMT 1912 Apr # Bermuda +# For 1899 Milne gives -4:19:18.3 as the meridian of the clock tower, +# Bermuda dockyard, Ireland I; round that. + # From Dan Jones, reporting in The Royal Gazette (2006-06-26): # Next year, however, clocks in the US will go forward on the second Sunday @@ -2626,7 +2637,7 @@ Zone America/Belize -5:52:48 - LMT 1912 Apr # http://www.theroyalgazette.com/apps/pbcs.dll/article?AID=/20060529/NEWS/105290135 # Zone NAME GMTOFF RULES FORMAT [UNTIL] -Zone Atlantic/Bermuda -4:19:04 - LMT 1930 Jan 1 2:00 # Hamilton +Zone Atlantic/Bermuda -4:19:18 - LMT 1930 Jan 1 2:00 # Hamilton -4:00 - AST 1974 Apr 28 2:00 -4:00 Bahamas A%sT 1976 -4:00 US A%sT @@ -2638,6 +2649,9 @@ Zone America/Cayman -5:25:32 - LMT 1890 # Georgetown -5:00 - EST # Costa Rica + +# Milne gives -5:36:13.3 as San Jose mean time; round to nearest. + # Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S Rule CR 1979 1980 - Feb lastSun 0:00 1:00 D Rule CR 1979 1980 - Jun Sun>=1 0:00 0 S @@ -2648,14 +2662,19 @@ Rule CR 1991 only - Jul 1 0:00 0 S Rule CR 1992 only - Mar 15 0:00 0 S # There are too many San Joses elsewhere, so we'll use `Costa Rica'. # Zone NAME GMTOFF RULES FORMAT [UNTIL] -Zone America/Costa_Rica -5:36:20 - LMT 1890 # San Jose - -5:36:20 - SJMT 1921 Jan 15 # San Jose Mean Time +Zone America/Costa_Rica -5:36:13 - LMT 1890 # San Jose + -5:36:13 - SJMT 1921 Jan 15 # San Jose Mean Time -6:00 CR C%sT # Coco # no information; probably like America/Costa_Rica # Cuba +# From Paul Eggert (2013-02-21): +# Milne gives -5:28:50.45 for the observatory at Havana, -5:29:23.57 +# for the port, and -5:30 for meteorological observations. +# For now, stick with Shanks & Pottenger. + # From Arthur David Olson (1999-03-29): # The 1999-03-28 exhibition baseball game held in Havana, Cuba, between # the Cuban National Team and the Baltimore Orioles was carried live on @@ -3004,24 +3023,21 @@ Zone America/Guatemala -6:02:04 - LMT 1918 Oct 5 # apparently using the same start and end date as USA/Canada. # So this means they have already changed their time. # -# (Sources in French): -# # http://www.alterpresse.org/spip.php?article12510 -# -# # http://radiovision2000haiti.net/home/?p=13253 -# # -# Our coverage: -# -# http://www.timeanddate.com/news/time/haiti-dst-2012.html -# - # From Arthur David Olson (2012-03-11): # The alterpresse.org source seems to show a US-style leap from 2:00 a.m. to # 3:00 a.m. rather than the traditional Haitian jump at midnight. -# Assume a US-style fall back as well XXX. -# Do not yet assume that the change carries forward past 2012 XXX. +# Assume a US-style fall back as well. + +# From Steffen Thorsen (2013-03-10): +# It appears that Haiti is observing DST this year as well, same rules +# as US/Canada. They did it last year as well, and it looks like they +# are going to observe DST every year now... +# +# http://radiovision2000haiti.net/public/haiti-avis-changement-dheure-dimanche/ +# http://www.canalplushaiti.net/?p=6714 # Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S Rule Haiti 1983 only - May 8 0:00 1:00 D @@ -3033,8 +3049,8 @@ Rule Haiti 1988 1997 - Apr Sun>=1 1:00s 1:00 D Rule Haiti 1988 1997 - Oct lastSun 1:00s 0 S Rule Haiti 2005 2006 - Apr Sun>=1 0:00 1:00 D Rule Haiti 2005 2006 - Oct lastSun 0:00 0 S -Rule Haiti 2012 only - Mar Sun>=8 2:00 1:00 D -Rule Haiti 2012 only - Nov Sun>=1 2:00 0 S +Rule Haiti 2012 max - Mar Sun>=8 2:00 1:00 D +Rule Haiti 2012 max - Nov Sun>=1 2:00 0 S # Zone NAME GMTOFF RULES FORMAT [UNTIL] Zone America/Port-au-Prince -4:49:20 - LMT 1890 -4:49 - PPMT 1917 Jan 24 12:00 # P-a-P MT diff --git a/jdk/make/sun/javazic/tzdata/southamerica b/jdk/make/sun/javazic/tzdata/southamerica index 0d6797eab6b..d1865d3f19b 100644 --- a/jdk/make/sun/javazic/tzdata/southamerica +++ b/jdk/make/sun/javazic/tzdata/southamerica @@ -27,13 +27,17 @@ # This data is by no means authoritative; if you think you know better, # go ahead and edit the file (and please send any changes to -# tz@elsie.nci.nih.gov for general use in the future). +# tz@iana.org for general use in the future). # From Paul Eggert (2006-03-22): # A good source for time zone historical data outside the U.S. is # Thomas G. Shanks and Rique Pottenger, The International Atlas (6th edition), # San Diego: ACS Publications, Inc. (2003). # +# For data circa 1899, a common source is: +# Milne J. Civil time. Geogr J. 1899 Feb;13(2):173-94 +# . +# # Gwillim Law writes that a good source # for recent time zone data is the International Air Transport # Association's Standard Schedules Information Manual (IATA SSIM), @@ -404,21 +408,11 @@ Rule Arg 2008 only - Oct Sun>=15 0:00 1:00 S # # is the official page for the Province Government). # -# There's also a note in only one of the major national papers (La Nación) at -# +# There's also a note in only one of the major national papers ... # http://www.lanacion.com.ar/nota.asp?nota_id=1107912 -# # -# The press release says: -# (...) anunció que el próximo domingo a las 00:00 los puntanos deberán -# atrasar una hora sus relojes. -# -# A partir de entonces, San Luis establecerá el huso horario propio de -# la Provincia. De esta manera, durante el periodo del calendario anual -# 2009, el cambio horario quedará comprendido entre las 00:00 del tercer -# domingo de marzo y las 24:00 del segundo sábado de octubre. -# Quick&dirty translation -# (...) announced that next Sunday, at 00:00, Puntanos (the San Luis +# The press release says [quick and dirty translation]: +# ... announced that next Sunday, at 00:00, Puntanos (the San Luis # inhabitants) will have to turn back one hour their clocks # # Since then, San Luis will establish its own Province timezone. Thus, @@ -480,6 +474,9 @@ Rule Arg 2008 only - Oct Sun>=15 0:00 1:00 S # rules...San Luis is still using "Western ARgentina Time" and it got # stuck on Summer daylight savings time even though the summer is over. +# From Paul Eggert (2013-02-21): +# Milne says Cordoba time was -4:16:48.2. Round to the nearest second. + # Zone NAME GMTOFF RULES FORMAT [UNTIL] # # Buenos Aires (BA), Capital Federal (CF), @@ -835,9 +832,9 @@ Zone America/La_Paz -4:32:36 - LMT 1890 # From Guilherme Bernardes Rodrigues (2011-10-07): # There is news in the media, however there is still no decree about it. -# I just send a e-mail to Zulmira Brandão at +# I just send a e-mail to Zulmira Brandao at # http://pcdsh01.on.br/ the -# oficial agency about time in Brazil, and she confirmed that the old rule is +# official agency about time in Brazil, and she confirmed that the old rule is # still in force. # From Guilherme Bernardes Rodrigues (2011-10-14) @@ -1266,9 +1263,13 @@ Zone America/Rio_Branco -4:31:12 - LMT 1914 # b. Saturday, September 1, 2012, clocks should go forward 60 minutes; that is, # at 23:59:59, instead of passing to 0:00, the time should be adjusted to be # 01:00 on September 2. -# -# Note that...this is yet another "temporary" change that will be reevaluated -# AGAIN in 2013. + +# From Steffen Thorsen (2013-02-15): +# According to several news sources, Chile has extended DST this year, +# they will end DST later and start DST earlier than planned. They +# hope to save energy. The new end date is 2013-04-28 00:00 and new +# start date is 2013-09-08 00:00.... +# http://www.gob.cl/informa/2013/02/15/gobierno-anuncia-fechas-de-cambio-de-hora-para-el-ano-2013.htm # NOTE: ChileAQ rules for Antarctic bases are stored separately in the # 'antarctica' file. @@ -1311,10 +1312,8 @@ Rule Chile 2009 only - Mar Sun>=9 3:00u 0 - Rule Chile 2010 only - Apr Sun>=1 3:00u 0 - Rule Chile 2011 only - May Sun>=2 3:00u 0 - Rule Chile 2011 only - Aug Sun>=16 4:00u 1:00 S -Rule Chile 2012 only - Apr Sun>=23 3:00u 0 - -Rule Chile 2012 only - Sep Sun>=2 4:00u 1:00 S -Rule Chile 2013 max - Mar Sun>=9 3:00u 0 - -Rule Chile 2013 max - Oct Sun>=9 4:00u 1:00 S +Rule Chile 2012 max - Apr Sun>=23 3:00u 0 - +Rule Chile 2012 max - Sep Sun>=2 4:00u 1:00 S # IATA SSIM anomalies: (1992-02) says 1992-03-14; # (1996-09) says 1998-03-08. Ignore these. # Zone NAME GMTOFF RULES FORMAT [UNTIL] @@ -1336,17 +1335,23 @@ Zone Pacific/Easter -7:17:44 - LMT 1890 # San Felix, and Antarctic bases, are like America/Santiago. # Colombia + +# Milne gives 4:56:16.4 for Bogota time in 1899; round to nearest. He writes, +# "A variation of fifteen minutes in the public clocks of Bogota is not rare." + # Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S Rule CO 1992 only - May 3 0:00 1:00 S Rule CO 1993 only - Apr 4 0:00 0 - # Zone NAME GMTOFF RULES FORMAT [UNTIL] -Zone America/Bogota -4:56:20 - LMT 1884 Mar 13 - -4:56:20 - BMT 1914 Nov 23 # Bogota Mean Time +Zone America/Bogota -4:56:16 - LMT 1884 Mar 13 + -4:56:16 - BMT 1914 Nov 23 # Bogota Mean Time -5:00 CO CO%sT # Colombia Time # Malpelo, Providencia, San Andres # no information; probably like America/Bogota # Curacao + +# Milne gives 4:35:46.9 for Curacao mean time; round to nearest. # # From Paul Eggert (2006-03-22): # Shanks & Pottenger say that The Bottom and Philipsburg have been at @@ -1363,7 +1368,7 @@ Zone America/Bogota -4:56:20 - LMT 1884 Mar 13 # though, as far as we know. # # Zone NAME GMTOFF RULES FORMAT [UNTIL] -Zone America/Curacao -4:35:44 - LMT 1912 Feb 12 # Willemstad +Zone America/Curacao -4:35:47 - LMT 1912 Feb 12 # Willemstad -4:30 - ANT 1965 # Netherlands Antilles Time -4:00 - AST @@ -1377,6 +1382,8 @@ Link America/Curacao America/Kralendijk # Bonaire, Sint Estatius and Saba # Ecuador # +# Milne says the Sentral and South American Telegraph Company used -5:24:15. +# # From Paul Eggert (2007-03-04): # Apparently Ecuador had a failed experiment with DST in 1992. # (2007-02-27) and @@ -1582,7 +1589,16 @@ Rule Para 2005 2009 - Mar Sun>=8 0:00 0 - # forward 60 minutes, in all the territory of the Paraguayan Republic. # ... Rule Para 2010 max - Oct Sun>=1 0:00 1:00 S -Rule Para 2010 max - Apr Sun>=8 0:00 0 - +Rule Para 2010 2012 - Apr Sun>=8 0:00 0 - +# +# From Steffen Thorsen (2013-03-07): +# Paraguay will end DST on 2013-03-24 00:00.... +# http://www.ande.gov.py/interna.php?id=1075 +# +# From Carlos Raul Perasso (2013-03-15): +# The change in Paraguay is now final. Decree number 10780 +# http://www.presidencia.gov.py/uploads/pdf/presidencia-3b86ff4b691c79d4f5927ca964922ec74772ce857c02ca054a52a37b49afc7fb.pdf +Rule Para 2013 max - Mar Sun>=22 0:00 0 - # Zone NAME GMTOFF RULES FORMAT [UNTIL] Zone America/Asuncion -3:50:40 - LMT 1890 diff --git a/jdk/make/sun/javazic/tzdata/zone.tab b/jdk/make/sun/javazic/tzdata/zone.tab index ef380cd19fb..cbcdc075bf7 100644 --- a/jdk/make/sun/javazic/tzdata/zone.tab +++ b/jdk/make/sun/javazic/tzdata/zone.tab @@ -65,7 +65,6 @@ AQ -6617+11031 Antarctica/Casey Casey Station, Bailey Peninsula AQ -7824+10654 Antarctica/Vostok Vostok Station, Lake Vostok AQ -6640+14001 Antarctica/DumontDUrville Dumont-d'Urville Station, Terre Adelie AQ -690022+0393524 Antarctica/Syowa Syowa Station, E Ongul I -AQ -5430+15857 Antarctica/Macquarie Macquarie Island Station, Macquarie Island AR -3436-05827 America/Argentina/Buenos_Aires Buenos Aires (BA, CF) AR -3124-06411 America/Argentina/Cordoba most locations (CB, CC, CN, ER, FM, MN, SE, SF) AR -2447-06525 America/Argentina/Salta (SA, LP, NQ, RN) @@ -81,6 +80,7 @@ AR -5448-06818 America/Argentina/Ushuaia Tierra del Fuego (TF) AS -1416-17042 Pacific/Pago_Pago AT +4813+01620 Europe/Vienna AU -3133+15905 Australia/Lord_Howe Lord Howe Island +AU -5430+15857 Antarctica/Macquarie Macquarie Island AU -4253+14719 Australia/Hobart Tasmania - most locations AU -3956+14352 Australia/Currie Tasmania - King Island AU -3749+14458 Australia/Melbourne Victoria @@ -182,7 +182,8 @@ CW +1211-06900 America/Curacao CX -1025+10543 Indian/Christmas CY +3510+03322 Asia/Nicosia CZ +5005+01426 Europe/Prague -DE +5230+01322 Europe/Berlin +DE +5230+01322 Europe/Berlin most locations +DE +4742+00841 Europe/Busingen Busingen DJ +1136+04309 Africa/Djibouti DK +5540+01235 Europe/Copenhagen DM +1518-06124 America/Dominica @@ -364,8 +365,10 @@ RU +5345+08707 Asia/Novokuznetsk Moscow+03 - Novokuznetsk RU +5601+09250 Asia/Krasnoyarsk Moscow+04 - Yenisei River RU +5216+10420 Asia/Irkutsk Moscow+05 - Lake Baikal RU +6200+12940 Asia/Yakutsk Moscow+06 - Lena River +RU +623923+1353314 Asia/Khandyga Moscow+06 - Tomponsky, Ust-Maysky RU +4310+13156 Asia/Vladivostok Moscow+07 - Amur River RU +4658+14242 Asia/Sakhalin Moscow+07 - Sakhalin Island +RU +643337+1431336 Asia/Ust-Nera Moscow+07 - Oymyakonsky RU +5934+15048 Asia/Magadan Moscow+08 - Magadan RU +5301+15839 Asia/Kamchatka Moscow+08 - Kamchatka RU +6445+17729 Asia/Anadyr Moscow+08 - Bering Sea diff --git a/jdk/make/sun/nio/cs/Makefile b/jdk/make/sun/nio/cs/Makefile index 73fe55fbd08..3b45c774fe0 100644 --- a/jdk/make/sun/nio/cs/Makefile +++ b/jdk/make/sun/nio/cs/Makefile @@ -87,9 +87,6 @@ build: $(FILES_genout_extcs) $(CHARSETS_JAR) # # Extra rules to build character converters. -SERVICE_DESCRIPTION = java.nio.charset.spi.CharsetProvider -SERVICE_DESCRIPTION_PATH = META-INF/services/$(SERVICE_DESCRIPTION) - GENCSDATASRC = $(BUILDDIR)/tools/CharsetMapping GENCSSRCDIR = $(BUILDDIR)/tools/src/build/tools/charsetmapping GENCSEXT = $(GENSRCDIR)/sun/nio/cs/ext @@ -118,10 +115,6 @@ $(FILES_genout_extcs): \ $(GENCSSRCDIR)/HKSCS.java $(BOOT_JAVA_CMD) -jar $(CHARSETMAPPING_JARFILE) $(GENCSDATASRC) $(GENCSEXT) dbcs -$(CLASSDESTDIR)/$(SERVICE_DESCRIPTION_PATH): \ - $(SHARE_SRC)/classes/sun/nio/cs/ext/$(SERVICE_DESCRIPTION_PATH) - $(install-file) - # no compression unless requested ifndef COMPRESS_JARS CREATE_JAR_OPTS_NOMANIFEST = cf0 @@ -129,10 +122,9 @@ else CREATE_JAR_OPTS_NOMANIFEST = cf endif -$(CHARSETS_JAR): $(FILES_class) $(CLASSDESTDIR)/$(SERVICE_DESCRIPTION_PATH) $(FILES_DAT) +$(CHARSETS_JAR): $(FILES_class) $(FILES_DAT) $(BOOT_JAR_CMD) $(CREATE_JAR_OPTS_NOMANIFEST) $(CHARSETS_JAR) \ -C $(CLASSDESTDIR) sun \ - -C $(CLASSDESTDIR) $(SERVICE_DESCRIPTION_PATH) \ $(BOOT_JAR_JFLAGS) @$(java-vm-cleanup) diff --git a/jdk/make/tools/src/build/tools/cldrconverter/CLDRConverter.java b/jdk/make/tools/src/build/tools/cldrconverter/CLDRConverter.java index f1185f18978..9cbf02a9c3b 100644 --- a/jdk/make/tools/src/build/tools/cldrconverter/CLDRConverter.java +++ b/jdk/make/tools/src/build/tools/cldrconverter/CLDRConverter.java @@ -34,6 +34,8 @@ import java.nio.file.Path; import java.util.*; import javax.xml.parsers.SAXParser; import javax.xml.parsers.SAXParserFactory; +import org.xml.sax.SAXNotRecognizedException; +import org.xml.sax.SAXNotSupportedException; /** @@ -234,6 +236,17 @@ public class CLDRConverter { } } + /** + * Configure the parser to allow access to DTDs on the file system. + */ + private static void enableFileAccess(SAXParser parser) throws SAXNotSupportedException { + try { + parser.setProperty("http://javax.xml.XMLConstants/property/accessExternalDTD", "file"); + } catch (SAXNotRecognizedException ignore) { + // property requires >= JAXP 1.5 + } + } + private static List readBundleList() throws Exception { ResourceBundle.Control defCon = ResourceBundle.Control.getControl(ResourceBundle.Control.FORMAT_DEFAULT); List retList = new ArrayList<>(); @@ -279,6 +292,7 @@ public class CLDRConverter { SAXParserFactory factory = SAXParserFactory.newInstance(); factory.setValidating(true); SAXParser parser = factory.newSAXParser(); + enableFileAccess(parser); LDMLParseHandler handler = new LDMLParseHandler(id); File file = new File(SOURCE_FILE_DIR + File.separator + id + ".xml"); if (!file.exists()) { @@ -314,6 +328,7 @@ public class CLDRConverter { SAXParserFactory factorySuppl = SAXParserFactory.newInstance(); factorySuppl.setValidating(true); SAXParser parserSuppl = factorySuppl.newSAXParser(); + enableFileAccess(parserSuppl); handlerSuppl = new SupplementDataParseHandler(); File fileSupply = new File(SPPL_SOURCE_FILE); parserSuppl.parse(fileSupply, handlerSuppl); @@ -322,6 +337,7 @@ public class CLDRConverter { SAXParserFactory numberingParser = SAXParserFactory.newInstance(); numberingParser.setValidating(true); SAXParser parserNumbering = numberingParser.newSAXParser(); + enableFileAccess(parserNumbering); handlerNumbering = new NumberingSystemsParseHandler(); File fileNumbering = new File(NUMBERING_SOURCE_FILE); parserNumbering.parse(fileNumbering, handlerNumbering); @@ -330,6 +346,7 @@ public class CLDRConverter { SAXParserFactory metazonesParser = SAXParserFactory.newInstance(); metazonesParser.setValidating(true); SAXParser parserMetaZones = metazonesParser.newSAXParser(); + enableFileAccess(parserMetaZones); handlerMetaZones = new MetaZonesParseHandler(); File fileMetaZones = new File(METAZONES_SOURCE_FILE); parserNumbering.parse(fileMetaZones, handlerMetaZones); diff --git a/jdk/makefiles/CreateJars.gmk b/jdk/makefiles/CreateJars.gmk index 8127b054459..764e63486f5 100644 --- a/jdk/makefiles/CreateJars.gmk +++ b/jdk/makefiles/CreateJars.gmk @@ -201,7 +201,6 @@ RT_JAR_EXCLUDES += \ META-INF/services/com.sun.jdi.connect.spi.TransportService \ META-INF/services/com.sun.tools.attach.spi.AttachProvider \ META-INF/services/com.sun.tools.xjc.Plugin \ - META-INF/services/java.nio.charset.spi.CharsetProvider \ META-INF/services/sun.net.spi.nameservice.NameServiceDescriptor \ org/relaxng/datatype \ sun/awt/HKSCS.class \ @@ -428,8 +427,7 @@ $(eval $(call SetupArchive,BUILD_CHARSETS_JAR,,\ SUFFIXES:=.class .dat,\ INCLUDES:=sun/nio/cs/ext,\ EXTRA_FILES := sun/awt/HKSCS.class \ - $(CHARSETS_EXTRA_FILES) \ - META-INF/services/java.nio.charset.spi.CharsetProvider, \ + $(CHARSETS_EXTRA_FILES), \ JAR:=$(IMAGES_OUTPUTDIR)/lib/charsets.jar, \ SKIP_METAINF := true, \ CHECK_COMPRESS_JAR:=true)) diff --git a/jdk/src/share/classes/java/lang/ProcessBuilder.java b/jdk/src/share/classes/java/lang/ProcessBuilder.java index 64f56d77829..b467f45c351 100644 --- a/jdk/src/share/classes/java/lang/ProcessBuilder.java +++ b/jdk/src/share/classes/java/lang/ProcessBuilder.java @@ -29,7 +29,6 @@ import java.io.File; import java.io.IOException; import java.io.InputStream; import java.io.OutputStream; -import java.io.FileOutputStream; import java.security.AccessControlException; import java.util.Arrays; import java.util.ArrayList; @@ -1024,10 +1023,10 @@ public final class ProcessBuilder dir, redirects, redirectErrorStream); - } catch (IOException e) { + } catch (IOException | IllegalArgumentException e) { String exceptionInfo = ": " + e.getMessage(); Throwable cause = e; - if (security != null) { + if ((e instanceof IOException) && security != null) { // Can not disclose the fail reason for read-protected files. try { security.checkRead(prog); diff --git a/jdk/src/share/classes/java/lang/StringBuffer.java b/jdk/src/share/classes/java/lang/StringBuffer.java index f31a53c98e6..8ead0721ffd 100644 --- a/jdk/src/share/classes/java/lang/StringBuffer.java +++ b/jdk/src/share/classes/java/lang/StringBuffer.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 1994, 2012, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 1994, 2013, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,6 +25,7 @@ package java.lang; +import java.util.Arrays; /** * A thread-safe, mutable sequence of characters. @@ -98,6 +99,12 @@ package java.lang; implements java.io.Serializable, CharSequence { + /** + * A cache of the last value returned by toString. Cleared + * whenever the StringBuffer is modified. + */ + private transient char[] toStringCache; + /** use serialVersionUID from JDK 1.0.2 for interoperability */ static final long serialVersionUID = 3388685877147921107L; @@ -183,6 +190,7 @@ package java.lang; */ @Override public synchronized void setLength(int newLength) { + toStringCache = null; super.setLength(newLength); } @@ -247,17 +255,20 @@ package java.lang; public synchronized void setCharAt(int index, char ch) { if ((index < 0) || (index >= count)) throw new StringIndexOutOfBoundsException(index); + toStringCache = null; value[index] = ch; } @Override public synchronized StringBuffer append(Object obj) { + toStringCache = null; super.append(String.valueOf(obj)); return this; } @Override public synchronized StringBuffer append(String str) { + toStringCache = null; super.append(str); return this; } @@ -287,6 +298,7 @@ package java.lang; * @since 1.4 */ public synchronized StringBuffer append(StringBuffer sb) { + toStringCache = null; super.append(sb); return this; } @@ -296,6 +308,7 @@ package java.lang; */ @Override synchronized StringBuffer append(AbstractStringBuilder asb) { + toStringCache = null; super.append(asb); return this; } @@ -325,6 +338,7 @@ package java.lang; public StringBuffer append(CharSequence s) { // Note, synchronization achieved via invocations of other StringBuffer methods after // narrowing of s to specific type + // Ditto for toStringCache clearing super.append(s); return this; } @@ -336,12 +350,14 @@ package java.lang; @Override public synchronized StringBuffer append(CharSequence s, int start, int end) { + toStringCache = null; super.append(s, start, end); return this; } @Override public synchronized StringBuffer append(char[] str) { + toStringCache = null; super.append(str); return this; } @@ -351,24 +367,28 @@ package java.lang; */ @Override public synchronized StringBuffer append(char[] str, int offset, int len) { + toStringCache = null; super.append(str, offset, len); return this; } @Override public synchronized StringBuffer append(boolean b) { + toStringCache = null; super.append(b); return this; } @Override public synchronized StringBuffer append(char c) { + toStringCache = null; super.append(c); return this; } @Override public synchronized StringBuffer append(int i) { + toStringCache = null; super.append(i); return this; } @@ -378,24 +398,28 @@ package java.lang; */ @Override public synchronized StringBuffer appendCodePoint(int codePoint) { + toStringCache = null; super.appendCodePoint(codePoint); return this; } @Override public synchronized StringBuffer append(long lng) { + toStringCache = null; super.append(lng); return this; } @Override public synchronized StringBuffer append(float f) { + toStringCache = null; super.append(f); return this; } @Override public synchronized StringBuffer append(double d) { + toStringCache = null; super.append(d); return this; } @@ -406,6 +430,7 @@ package java.lang; */ @Override public synchronized StringBuffer delete(int start, int end) { + toStringCache = null; super.delete(start, end); return this; } @@ -416,6 +441,7 @@ package java.lang; */ @Override public synchronized StringBuffer deleteCharAt(int index) { + toStringCache = null; super.deleteCharAt(index); return this; } @@ -426,6 +452,7 @@ package java.lang; */ @Override public synchronized StringBuffer replace(int start, int end, String str) { + toStringCache = null; super.replace(start, end, str); return this; } @@ -465,6 +492,7 @@ package java.lang; public synchronized StringBuffer insert(int index, char[] str, int offset, int len) { + toStringCache = null; super.insert(index, str, offset, len); return this; } @@ -474,6 +502,7 @@ package java.lang; */ @Override public synchronized StringBuffer insert(int offset, Object obj) { + toStringCache = null; super.insert(offset, String.valueOf(obj)); return this; } @@ -483,6 +512,7 @@ package java.lang; */ @Override public synchronized StringBuffer insert(int offset, String str) { + toStringCache = null; super.insert(offset, str); return this; } @@ -492,6 +522,7 @@ package java.lang; */ @Override public synchronized StringBuffer insert(int offset, char[] str) { + toStringCache = null; super.insert(offset, str); return this; } @@ -504,6 +535,7 @@ package java.lang; public StringBuffer insert(int dstOffset, CharSequence s) { // Note, synchronization achieved via invocations of other StringBuffer methods // after narrowing of s to specific type + // Ditto for toStringCache clearing super.insert(dstOffset, s); return this; } @@ -516,6 +548,7 @@ package java.lang; public synchronized StringBuffer insert(int dstOffset, CharSequence s, int start, int end) { + toStringCache = null; super.insert(dstOffset, s, start, end); return this; } @@ -527,6 +560,7 @@ package java.lang; public StringBuffer insert(int offset, boolean b) { // Note, synchronization achieved via invocation of StringBuffer insert(int, String) // after conversion of b to String by super class method + // Ditto for toStringCache clearing super.insert(offset, b); return this; } @@ -536,6 +570,7 @@ package java.lang; */ @Override public synchronized StringBuffer insert(int offset, char c) { + toStringCache = null; super.insert(offset, c); return this; } @@ -547,6 +582,7 @@ package java.lang; public StringBuffer insert(int offset, int i) { // Note, synchronization achieved via invocation of StringBuffer insert(int, String) // after conversion of i to String by super class method + // Ditto for toStringCache clearing super.insert(offset, i); return this; } @@ -558,6 +594,7 @@ package java.lang; public StringBuffer insert(int offset, long l) { // Note, synchronization achieved via invocation of StringBuffer insert(int, String) // after conversion of l to String by super class method + // Ditto for toStringCache clearing super.insert(offset, l); return this; } @@ -569,6 +606,7 @@ package java.lang; public StringBuffer insert(int offset, float f) { // Note, synchronization achieved via invocation of StringBuffer insert(int, String) // after conversion of f to String by super class method + // Ditto for toStringCache clearing super.insert(offset, f); return this; } @@ -580,6 +618,7 @@ package java.lang; public StringBuffer insert(int offset, double d) { // Note, synchronization achieved via invocation of StringBuffer insert(int, String) // after conversion of d to String by super class method + // Ditto for toStringCache clearing super.insert(offset, d); return this; } @@ -623,13 +662,17 @@ package java.lang; */ @Override public synchronized StringBuffer reverse() { + toStringCache = null; super.reverse(); return this; } @Override public synchronized String toString() { - return new String(value, 0, count); + if (toStringCache == null) { + toStringCache = Arrays.copyOfRange(value, 0, count); + } + return new String(toStringCache, true); } /** diff --git a/jdk/src/share/classes/java/net/HttpURLConnection.java b/jdk/src/share/classes/java/net/HttpURLConnection.java index d52c26789fa..193b9cef1c1 100644 --- a/jdk/src/share/classes/java/net/HttpURLConnection.java +++ b/jdk/src/share/classes/java/net/HttpURLConnection.java @@ -50,6 +50,18 @@ import java.util.Date; * Proxy settings as well as * various other settings. *

+ *

+ * Security permissions + *

+ * If a security manager is installed, and if a method is called which results in an + * attempt to open a connection, the caller must possess either:- + *

  • a "connect" {@link SocketPermission} to the host/port combination of the + * destination URL or
  • + *
  • a {@link HttpURLPermission} that permits this request.
  • + *

+ * If automatic redirection is enabled, and this request is redirected to another + * destination, then the caller must also have permission to connect to the + * redirected host/URL. * * @see java.net.HttpURLConnection#disconnect() * @since JDK1.1 diff --git a/jdk/src/share/classes/java/net/HttpURLPermission.java b/jdk/src/share/classes/java/net/HttpURLPermission.java new file mode 100644 index 00000000000..52d6e79344a --- /dev/null +++ b/jdk/src/share/classes/java/net/HttpURLPermission.java @@ -0,0 +1,406 @@ +/* + * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package java.net; + +import java.io.ObjectInputStream; +import java.io.IOException; +import java.util.List; +import java.util.ArrayList; +import java.util.Collections; +import java.security.Permission; + +/** + * Represents permission to access a resource or set of resources defined by a + * given http or https url, and for a given set of user-settable request methods + * and request headers. The name of the permission is the url string. + * The actions string is a concatenation of the request methods and headers. + * The range of method and header names is not restricted by this class. + *

The url

+ * The url string is also used to instantiate a {@link URI} object which is + * used for comparison with other HttpURLPermission instances. Therefore, any + * references in this specification to url, mean this URI object. + * The path component of the url comprises a sequence of path segments, separated + * by '/' characters. The path is specified in a similar way to the path + * in {@link java.io.FilePermission}. There are three different ways + * as the following examples show: + * + * + * + * + * + * + * + * + * + * + *
Example urlDescription
http://www.oracle.com/a/b/c.htmlA url which identifies a specific (single) resource
http://www.oracle.com/a/b/*The '*' character refers to all resources in the same "directory" - in + * other words all resources with the same number of path components, and + * which only differ in the final path component, represented by the '*'. + *
http://www.oracle.com/a/b/- + * The '-' character refers to all resources recursively below the + * preceding path (eg. http://www.oracle.com/a/b/c/d/e.html matches this + * example). + *
+ *

+ * The '*' and '-' may only be specified in the final segment of a path and must be + * the only character in that segment. Any query or fragment components of the + * url are ignored when constructing HttpURLPermissions. + *

+ * As a special case, urls of the form, "http:*" or "https:*" are accepted to + * mean any url of the given scheme. + *

The actions string

+ * The actions string of a HttpURLPermission is a concatenation of the method list + * and the request headers list. These are lists of the permitted HTTP request + * methods and permitted request headers of the permission (respectively). The two lists + * are separated by a colon ':' character and elements of each list are comma separated. + * Some examples are: + *

+ *         "POST,GET,DELETE"
+ *         "GET:X-Foo-Request,X-Bar-Request"
+ *         "POST,GET:Header1,Header2"
+ * 
+ * The first example specifies the methods: POST, GET and DELETE, but no request headers. + * The second example specifies one request method and two headers. The third + * example specifies two request methods, and two headers. + *

+ * The colon separator need not be present if the request headers list is empty. + * No white-space is permitted in the actions string. The action strings supplied to + * the HttpURLPermission constructors are case-insensitive and are normalized by converting + * method names to upper-case and header names to the form defines in RFC2616 (lower case + * with initial letter of each word capitalized). Either list can contain a wild-card '*' + * character which signifies all request methods or headers respectively. + *

+ * Note. Depending on the context of use, some request methods and headers may be permitted + * at all times, and others may not be permitted at any time. For example, the + * HTTP protocol handler might disallow certain headers such as Content-Length + * from being set by application code, regardless of whether the security policy + * in force, permits it. + * + * @since 1.8 + */ +public final class HttpURLPermission extends Permission { + + private static final long serialVersionUID = -2702463814894478682L; + + private transient URI uri; + private transient List methods; + private transient List requestHeaders; + + // serialized field + private String actions; + + /** + * Creates a new HttpURLPermission from a url string and which permits the given + * request methods and user-settable request headers. + * The name of the permission is its url string. Only the scheme, authority + * and path components of the url are used. Any fragment or query + * components are ignored. The permissions action string is as specified above. + * + * @param url the url string + * + * @param actions the actions string + * + * @throws IllegalArgumentException if url does not result in a valid {@link URI}, + * its scheme is not http or https, or if actions contains white-space. + */ + public HttpURLPermission(String url, String actions) { + super(url); + init(actions); + } + + private void init(String actions) { + URI uri = parseURI(getName()); + int colon = actions.indexOf(':'); + if (actions.lastIndexOf(':') != colon) { + throw new IllegalArgumentException("invalid actions string"); + } + + String methods, headers; + if (colon == -1) { + methods = actions; + headers = ""; + } else { + methods = actions.substring(0, colon); + headers = actions.substring(colon+1); + } + + List l = normalizeMethods(methods); + Collections.sort(l); + this.methods = Collections.unmodifiableList(l); + + l = normalizeHeaders(headers); + Collections.sort(l); + this.requestHeaders = Collections.unmodifiableList(l); + + this.actions = actions(); + this.uri = uri; + } + + /** + * Creates a HttpURLPermission with the given url string and unrestricted + * methods and request headers by invoking the two argument + * constructor as follows: HttpURLPermission(url, "*:*") + * + * @throws IllegalArgumentException if url does not result in a valid {@link URI} + */ + public HttpURLPermission(String url) { + this(url, "*:*"); + } + + /** + * Returns the normalized method list and request + * header list, in the form: + *

+     *      "method-names : header-names"
+     * 
+ *

+ * where method-names is the list of methods separated by commas + * and header-names is the list of permitted headers separated by commas. + * There is no white space in the returned String. If header-names is empty + * then the colon separator will not be present. + */ + public String getActions() { + return actions; + } + + /** + * Checks if this HttpURLPermission implies the given permission. + * Specifically, the following checks are done as if in the + * following sequence: + *

    + *
  • if 'p' is not an instance of HttpURLPermission return false
  • + *
  • if any of p's methods are not in this's method list, and if + * this's method list is not equal to "*", then return false.
  • + *
  • if any of p's headers are not in this's request header list, and if + * this's request header list is not equal to "*", then return false.
  • + *
  • if this's url is equal to p's url , then return true
  • + *
  • if this's url scheme is not equal to p's url scheme return false
  • + *
  • if the scheme specific part of this's url is '*' return true
  • + *
  • if this's url authority is not equal to p's url authority + * return false
  • + *
  • if the path or paths specified by p's url are contained in the + * set of paths specified by this's url, then return true + *
  • otherwise, return false
  • + * + *

    + * Some examples of how paths are matched are shown below: + *

    + * + * + * + * + * + * + * + * + * + *
    this's pathp's pathmatch
    /a/b/a/byes
    /a/b/*/a/b/cyes
    /a/b/*/a/b/c/dno
    /a/b/-/a/b/c/dyes
    /a/b/-/a/b/c/d/eyes
    /a/b/-/a/b/c/*yes
    /a/b/*/a/b/c/-no
    + */ + public boolean implies(Permission p) { + if (! (p instanceof HttpURLPermission)) { + return false; + } + + HttpURLPermission that = (HttpURLPermission)p; + + if (!this.methods.get(0).equals("*") && + Collections.indexOfSubList(this.methods, that.methods) == -1) { + return false; + } + + if (this.requestHeaders.isEmpty() && !that.requestHeaders.isEmpty()) { + return false; + } + + if (!this.requestHeaders.isEmpty() && + !this.requestHeaders.get(0).equals("*") && + Collections.indexOfSubList(this.requestHeaders, + that.requestHeaders) == -1) { + return false; + } + + if (this.uri.equals(that.uri)) { + return true; + } + + if (!this.uri.getScheme().equals(that.uri.getScheme())) { + return false; + } + + if (this.uri.getSchemeSpecificPart().equals("*")) { + return true; + } + + String thisAuthority = this.uri.getAuthority(); + + if (thisAuthority != null && + !thisAuthority.equals(that.uri.getAuthority())) { + return false; + } + + String thispath = this.uri.getPath(); + String thatpath = that.uri.getPath(); + + if (thispath.endsWith("/-")) { + String thisprefix = thispath.substring(0, thispath.length() - 1); + return thatpath.startsWith(thisprefix); + } + + if (thispath.endsWith("/*")) { + String thisprefix = thispath.substring(0, thispath.length() - 1); + if (!thatpath.startsWith(thisprefix)) { + return false; + } + String thatsuffix = thatpath.substring(thisprefix.length()); + // suffix must not contain '/' chars + if (thatsuffix.indexOf('/') != -1) { + return false; + } + if (thatsuffix.equals("-")) { + return false; + } + return true; + } + return false; + } + + + /** + * Returns true if, this.getActions().equals(p.getActions()) + * and p's url equals this's url. Returns false otherwise. + */ + public boolean equals(Object p) { + if (!(p instanceof HttpURLPermission)) { + return false; + } + HttpURLPermission that = (HttpURLPermission)p; + return this.getActions().equals(that.getActions()) && + this.uri.equals(that.uri); + } + + /** + * Returns a hashcode calculated from the hashcode of the + * actions String and the url + */ + public int hashCode() { + return getActions().hashCode() + uri.hashCode(); + } + + + private List normalizeMethods(String methods) { + List l = new ArrayList<>(); + StringBuilder b = new StringBuilder(); + for (int i=0; i 0) + l.add(s); + b = new StringBuilder(); + } else if (c == ' ' || c == '\t') { + throw new IllegalArgumentException("white space not allowed"); + } else { + if (c >= 'a' && c <= 'z') { + c += 'A' - 'a'; + } + b.append(c); + } + } + String s = b.toString(); + if (s.length() > 0) + l.add(s); + return l; + } + + private List normalizeHeaders(String headers) { + List l = new ArrayList<>(); + StringBuilder b = new StringBuilder(); + boolean capitalizeNext = true; + for (int i=0; i= 'a' && c <= 'z') { + if (capitalizeNext) { + c += 'A' - 'a'; + capitalizeNext = false; + } + b.append(c); + } else if (c == ' ' || c == '\t') { + throw new IllegalArgumentException("white space not allowed"); + } else if (c == '-') { + capitalizeNext = true; + b.append(c); + } else if (c == ',') { + String s = b.toString(); + if (s.length() > 0) + l.add(s); + b = new StringBuilder(); + capitalizeNext = true; + } else { + capitalizeNext = false; + b.append(c); + } + } + String s = b.toString(); + if (s.length() > 0) + l.add(s); + return l; + } + + private URI parseURI(String url) { + URI u = URI.create(url); + String scheme = u.getScheme(); + if (!(scheme.equalsIgnoreCase("http") || + scheme.equalsIgnoreCase("https"))) { + throw new IllegalArgumentException ("unexpected URL scheme"); + } + if (!u.getSchemeSpecificPart().equals("*")) { + u = URI.create(scheme + "://" + u.getAuthority() + u.getPath()); + } + return u; + } + + private String actions() { + StringBuilder b = new StringBuilder(); + for (String s : methods) { + b.append(s); + } + b.append(":"); + for (String s : requestHeaders) { + b.append(s); + } + return b.toString(); + } + /** + * restore the state of this object from stream + */ + private void readObject(ObjectInputStream s) + throws IOException, ClassNotFoundException { + ObjectInputStream.GetField fields = s.readFields(); + String actions = (String)fields.get("actions", null); + + init(actions); + } +} diff --git a/jdk/src/share/classes/java/net/ServerSocket.java b/jdk/src/share/classes/java/net/ServerSocket.java index 887a1e7de5f..b47e4420e44 100644 --- a/jdk/src/share/classes/java/net/ServerSocket.java +++ b/jdk/src/share/classes/java/net/ServerSocket.java @@ -607,9 +607,9 @@ class ServerSocket implements java.io.Closeable { } /** - * Enable/disable SO_TIMEOUT with the specified timeout, in - * milliseconds. With this option set to a non-zero timeout, - * a call to accept() for this ServerSocket + * Enable/disable {@link SocketOptions#SO_TIMEOUT SO_TIMEOUT} with the + * specified timeout, in milliseconds. With this option set to a non-zero + * timeout, a call to accept() for this ServerSocket * will block for only this amount of time. If the timeout expires, * a java.net.SocketTimeoutException is raised, though the * ServerSocket is still valid. The option must be enabled @@ -629,9 +629,9 @@ class ServerSocket implements java.io.Closeable { } /** - * Retrieve setting for SO_TIMEOUT. 0 returns implies that the - * option is disabled (i.e., timeout of infinity). - * @return the SO_TIMEOUT value + * Retrieve setting for {@link SocketOptions#SO_TIMEOUT SO_TIMEOUT}. + * 0 returns implies that the option is disabled (i.e., timeout of infinity). + * @return the {@link SocketOptions#SO_TIMEOUT SO_TIMEOUT} value * @exception IOException if an I/O error occurs * @since JDK1.1 * @see #setSoTimeout(int) @@ -649,7 +649,8 @@ class ServerSocket implements java.io.Closeable { } /** - * Enable/disable the SO_REUSEADDR socket option. + * Enable/disable the {@link SocketOptions#SO_REUSEADDR SO_REUSEADDR} + * socket option. *

    * When a TCP connection is closed the connection may remain * in a timeout state for a period of time after the connection @@ -660,24 +661,23 @@ class ServerSocket implements java.io.Closeable { * SocketAddress if there is a connection in the * timeout state involving the socket address or port. *

    - * Enabling SO_REUSEADDR prior to binding the socket - * using {@link #bind(SocketAddress)} allows the socket to be - * bound even though a previous connection is in a timeout - * state. + * Enabling {@link SocketOptions#SO_REUSEADDR SO_REUSEADDR} prior to + * binding the socket using {@link #bind(SocketAddress)} allows the socket + * to be bound even though a previous connection is in a timeout state. *

    * When a ServerSocket is created the initial setting - * of SO_REUSEADDR is not defined. Applications can - * use {@link #getReuseAddress()} to determine the initial - * setting of SO_REUSEADDR. + * of {@link SocketOptions#SO_REUSEADDR SO_REUSEADDR} is not defined. + * Applications can use {@link #getReuseAddress()} to determine the initial + * setting of {@link SocketOptions#SO_REUSEADDR SO_REUSEADDR}. *

    - * The behaviour when SO_REUSEADDR is enabled or - * disabled after a socket is bound (See {@link #isBound()}) + * The behaviour when {@link SocketOptions#SO_REUSEADDR SO_REUSEADDR} is + * enabled or disabled after a socket is bound (See {@link #isBound()}) * is not defined. * * @param on whether to enable or disable the socket option * @exception SocketException if an error occurs enabling or - * disabling the SO_RESUEADDR socket option, - * or the socket is closed. + * disabling the {@link SocketOptions#SO_REUSEADDR SO_REUSEADDR} + * socket option, or the socket is closed. * @since 1.4 * @see #getReuseAddress() * @see #bind(SocketAddress) @@ -691,9 +691,10 @@ class ServerSocket implements java.io.Closeable { } /** - * Tests if SO_REUSEADDR is enabled. + * Tests if {@link SocketOptions#SO_REUSEADDR SO_REUSEADDR} is enabled. * - * @return a boolean indicating whether or not SO_REUSEADDR is enabled. + * @return a boolean indicating whether or not + * {@link SocketOptions#SO_REUSEADDR SO_REUSEADDR} is enabled. * @exception SocketException if there is an error * in the underlying protocol, such as a TCP error. * @since 1.4 @@ -768,15 +769,16 @@ class ServerSocket implements java.io.Closeable { } /** - * Sets a default proposed value for the SO_RCVBUF option for sockets + * Sets a default proposed value for the + * {@link SocketOptions#SO_RCVBUF SO_RCVBUF} option for sockets * accepted from this ServerSocket. The value actually set * in the accepted socket must be determined by calling * {@link Socket#getReceiveBufferSize()} after the socket * is returned by {@link #accept()}. *

    - * The value of SO_RCVBUF is used both to set the size of the internal - * socket receive buffer, and to set the size of the TCP receive window - * that is advertized to the remote peer. + * The value of {@link SocketOptions#SO_RCVBUF SO_RCVBUF} is used both to + * set the size of the internal socket receive buffer, and to set the size + * of the TCP receive window that is advertized to the remote peer. *

    * It is possible to change the value subsequently, by calling * {@link Socket#setReceiveBufferSize(int)}. However, if the application @@ -812,15 +814,16 @@ class ServerSocket implements java.io.Closeable { } /** - * Gets the value of the SO_RCVBUF option for this ServerSocket, - * that is the proposed buffer size that will be used for Sockets accepted - * from this ServerSocket. + * Gets the value of the {@link SocketOptions#SO_RCVBUF SO_RCVBUF} option + * for this ServerSocket, that is the proposed buffer size that + * will be used for Sockets accepted from this ServerSocket. * *

    Note, the value actually set in the accepted socket is determined by * calling {@link Socket#getReceiveBufferSize()}. - * @return the value of the SO_RCVBUF option for this Socket. + * @return the value of the {@link SocketOptions#SO_RCVBUF SO_RCVBUF} + * option for this Socket. * @exception SocketException if there is an error - * in the underlying protocol, such as a TCP error. + * in the underlying protocol, such as a TCP error. * @see #setReceiveBufferSize(int) * @since 1.4 */ diff --git a/jdk/src/share/classes/java/net/Socket.java b/jdk/src/share/classes/java/net/Socket.java index d4f28e9ab6d..e0635c1bf9f 100644 --- a/jdk/src/share/classes/java/net/Socket.java +++ b/jdk/src/share/classes/java/net/Socket.java @@ -924,7 +924,8 @@ class Socket implements java.io.Closeable { } /** - * Enable/disable TCP_NODELAY (disable/enable Nagle's algorithm). + * Enable/disable {@link SocketOptions#TCP_NODELAY TCP_NODELAY} + * (disable/enable Nagle's algorithm). * * @param on true to enable TCP_NODELAY, * false to disable. @@ -943,9 +944,10 @@ class Socket implements java.io.Closeable { } /** - * Tests if TCP_NODELAY is enabled. + * Tests if {@link SocketOptions#TCP_NODELAY TCP_NODELAY} is enabled. * - * @return a boolean indicating whether or not TCP_NODELAY is enabled. + * @return a boolean indicating whether or not + * {@link SocketOptions#TCP_NODELAY TCP_NODELAY} is enabled. * @exception SocketException if there is an error * in the underlying protocol, such as a TCP error. * @since JDK1.1 @@ -958,8 +960,9 @@ class Socket implements java.io.Closeable { } /** - * Enable/disable SO_LINGER with the specified linger time in seconds. - * The maximum timeout value is platform specific. + * Enable/disable {@link SocketOptions#SO_LINGER SO_LINGER} with the + * specified linger time in seconds. The maximum timeout value is platform + * specific. * * The setting only affects socket close. * @@ -987,12 +990,13 @@ class Socket implements java.io.Closeable { } /** - * Returns setting for SO_LINGER. -1 returns implies that the + * Returns setting for {@link SocketOptions#SO_LINGER SO_LINGER}. + * -1 returns implies that the * option is disabled. * * The setting only affects socket close. * - * @return the setting for SO_LINGER. + * @return the setting for {@link SocketOptions#SO_LINGER SO_LINGER}. * @exception SocketException if there is an error * in the underlying protocol, such as a TCP error. * @since JDK1.1 @@ -1027,7 +1031,8 @@ class Socket implements java.io.Closeable { } /** - * Enable/disable OOBINLINE (receipt of TCP urgent data) + * Enable/disable {@link SocketOptions#SO_OOBINLINE SO_OOBINLINE} + * (receipt of TCP urgent data) * * By default, this option is disabled and TCP urgent data received on a * socket is silently discarded. If the user wishes to receive urgent data, then @@ -1039,8 +1044,9 @@ class Socket implements java.io.Closeable { * and there is no capability to distinguish between normal data and urgent * data unless provided by a higher level protocol. * - * @param on true to enable OOBINLINE, - * false to disable. + * @param on true to enable + * {@link SocketOptions#SO_OOBINLINE SO_OOBINLINE}, + * false to disable. * * @exception SocketException if there is an error * in the underlying protocol, such as a TCP error. @@ -1056,9 +1062,11 @@ class Socket implements java.io.Closeable { } /** - * Tests if OOBINLINE is enabled. + * Tests if {@link SocketOptions#SO_OOBINLINE SO_OOBINLINE} is enabled. + * + * @return a boolean indicating whether or not + * {@link SocketOptions#SO_OOBINLINE SO_OOBINLINE}is enabled. * - * @return a boolean indicating whether or not OOBINLINE is enabled. * @exception SocketException if there is an error * in the underlying protocol, such as a TCP error. * @since 1.4 @@ -1071,15 +1079,16 @@ class Socket implements java.io.Closeable { } /** - * Enable/disable SO_TIMEOUT with the specified timeout, in - * milliseconds. With this option set to a non-zero timeout, - * a read() call on the InputStream associated with this Socket - * will block for only this amount of time. If the timeout expires, - * a java.net.SocketTimeoutException is raised, though the + * Enable/disable {@link SocketOptions#SO_TIMEOUT SO_TIMEOUT} + * with the specified timeout, in milliseconds. With this option set + * to a non-zero timeout, a read() call on the InputStream associated with + * this Socket will block for only this amount of time. If the timeout + * expires, a java.net.SocketTimeoutException is raised, though the * Socket is still valid. The option must be enabled * prior to entering the blocking operation to have effect. The * timeout must be > 0. * A timeout of zero is interpreted as an infinite timeout. + * * @param timeout the specified timeout, in milliseconds. * @exception SocketException if there is an error * in the underlying protocol, such as a TCP error. @@ -1096,11 +1105,13 @@ class Socket implements java.io.Closeable { } /** - * Returns setting for SO_TIMEOUT. 0 returns implies that the - * option is disabled (i.e., timeout of infinity). - * @return the setting for SO_TIMEOUT + * Returns setting for {@link SocketOptions#SO_TIMEOUT SO_TIMEOUT}. + * 0 returns implies that the option is disabled (i.e., timeout of infinity). + * + * @return the setting for {@link SocketOptions#SO_TIMEOUT SO_TIMEOUT} * @exception SocketException if there is an error * in the underlying protocol, such as a TCP error. + * * @since JDK1.1 * @see #setSoTimeout(int) */ @@ -1117,14 +1128,15 @@ class Socket implements java.io.Closeable { } /** - * Sets the SO_SNDBUF option to the specified value for this - * Socket. The SO_SNDBUF option is used by the platform's - * networking code as a hint for the size to set - * the underlying network I/O buffers. + * Sets the {@link SocketOptions#SO_SNDBUF SO_SNDBUF} option to the + * specified value for this Socket. + * The {@link SocketOptions#SO_SNDBUF SO_SNDBUF} option is used by the + * platform's networking code as a hint for the size to set the underlying + * network I/O buffers. * - *

    Because SO_SNDBUF is a hint, applications that want to - * verify what size the buffers were set to should call - * {@link #getSendBufferSize()}. + *

    Because {@link SocketOptions#SO_SNDBUF SO_SNDBUF} is a hint, + * applications that want to verify what size the buffers were set to + * should call {@link #getSendBufferSize()}. * * @exception SocketException if there is an error * in the underlying protocol, such as a TCP error. @@ -1149,10 +1161,11 @@ class Socket implements java.io.Closeable { } /** - * Get value of the SO_SNDBUF option for this Socket, - * that is the buffer size used by the platform + * Get value of the {@link SocketOptions#SO_SNDBUF SO_SNDBUF} option + * for this Socket, that is the buffer size used by the platform * for output on this Socket. - * @return the value of the SO_SNDBUF option for this Socket. + * @return the value of the {@link SocketOptions#SO_SNDBUF SO_SNDBUF} + * option for this Socket. * * @exception SocketException if there is an error * in the underlying protocol, such as a TCP error. @@ -1172,25 +1185,26 @@ class Socket implements java.io.Closeable { } /** - * Sets the SO_RCVBUF option to the specified value for this - * Socket. The SO_RCVBUF option is used by the platform's - * networking code as a hint for the size to set + * Sets the {@link SocketOptions#SO_RCVBUF SO_RCVBUF} option to the + * specified value for this Socket. The + * {@link SocketOptions#SO_RCVBUF SO_RCVBUF} option is + * used by the platform's networking code as a hint for the size to set * the underlying network I/O buffers. * *

    Increasing the receive buffer size can increase the performance of * network I/O for high-volume connection, while decreasing it can * help reduce the backlog of incoming data. * - *

    Because SO_RCVBUF is a hint, applications that want to - * verify what size the buffers were set to should call - * {@link #getReceiveBufferSize()}. + *

    Because {@link SocketOptions#SO_RCVBUF SO_RCVBUF} is a hint, + * applications that want to verify what size the buffers were set to + * should call {@link #getReceiveBufferSize()}. * - *

    The value of SO_RCVBUF is also used to set the TCP receive window - * that is advertized to the remote peer. Generally, the window size - * can be modified at any time when a socket is connected. However, if - * a receive window larger than 64K is required then this must be requested - * before the socket is connected to the remote peer. There are two - * cases to be aware of:

    + *

    The value of {@link SocketOptions#SO_RCVBUF SO_RCVBUF} is also used + * to set the TCP receive window that is advertized to the remote peer. + * Generally, the window size can be modified at any time when a socket is + * connected. However, if a receive window larger than 64K is required then + * this must be requested before the socket is connected to the + * remote peer. There are two cases to be aware of:

    *

      *
    1. For sockets accepted from a ServerSocket, this must be done by calling * {@link ServerSocket#setReceiveBufferSize(int)} before the ServerSocket @@ -1221,11 +1235,12 @@ class Socket implements java.io.Closeable { } /** - * Gets the value of the SO_RCVBUF option for this Socket, - * that is the buffer size used by the platform for - * input on this Socket. + * Gets the value of the {@link SocketOptions#SO_RCVBUF SO_RCVBUF} option + * for this Socket, that is the buffer size used by the platform + * for input on this Socket. * - * @return the value of the SO_RCVBUF option for this Socket. + * @return the value of the {@link SocketOptions#SO_RCVBUF SO_RCVBUF} + * option for this Socket. * @exception SocketException if there is an error * in the underlying protocol, such as a TCP error. * @see #setReceiveBufferSize(int) @@ -1244,9 +1259,9 @@ class Socket implements java.io.Closeable { } /** - * Enable/disable SO_KEEPALIVE. + * Enable/disable {@link SocketOptions#SO_KEEPALIVE SO_KEEPALIVE}. * - * @param on whether or not to have socket keep alive turned on. + * @param on whether or not to have socket keep alive turned on. * @exception SocketException if there is an error * in the underlying protocol, such as a TCP error. * @since 1.3 @@ -1259,9 +1274,10 @@ class Socket implements java.io.Closeable { } /** - * Tests if SO_KEEPALIVE is enabled. + * Tests if {@link SocketOptions#SO_KEEPALIVE SO_KEEPALIVE} is enabled. * - * @return a boolean indicating whether or not SO_KEEPALIVE is enabled. + * @return a boolean indicating whether or not + * {@link SocketOptions#SO_KEEPALIVE SO_KEEPALIVE} is enabled. * @exception SocketException if there is an error * in the underlying protocol, such as a TCP error. * @since 1.3 @@ -1317,6 +1333,7 @@ class Socket implements java.io.Closeable { * traffic class or type-of-service * @since 1.4 * @see #getTrafficClass + * @see SocketOptions#IP_TOS */ public void setTrafficClass(int tc) throws SocketException { if (tc < 0 || tc > 255) @@ -1341,13 +1358,15 @@ class Socket implements java.io.Closeable { * traffic class or type-of-service value. * @since 1.4 * @see #setTrafficClass(int) + * @see SocketOptions#IP_TOS */ public int getTrafficClass() throws SocketException { return ((Integer) (getImpl().getOption(SocketOptions.IP_TOS))).intValue(); } /** - * Enable/disable the SO_REUSEADDR socket option. + * Enable/disable the {@link SocketOptions#SO_REUSEADDR SO_REUSEADDR} + * socket option. *

      * When a TCP connection is closed the connection may remain * in a timeout state for a period of time after the connection @@ -1358,22 +1377,22 @@ class Socket implements java.io.Closeable { * SocketAddress if there is a connection in the * timeout state involving the socket address or port. *

      - * Enabling SO_REUSEADDR prior to binding the socket - * using {@link #bind(SocketAddress)} allows the socket to be - * bound even though a previous connection is in a timeout + * Enabling {@link SocketOptions#SO_REUSEADDR SO_REUSEADDR} + * prior to binding the socket using {@link #bind(SocketAddress)} allows + * the socket to be bound even though a previous connection is in a timeout * state. *

      * When a Socket is created the initial setting - * of SO_REUSEADDR is disabled. + * of {@link SocketOptions#SO_REUSEADDR SO_REUSEADDR} is disabled. *

      - * The behaviour when SO_REUSEADDR is enabled or - * disabled after a socket is bound (See {@link #isBound()}) + * The behaviour when {@link SocketOptions#SO_REUSEADDR SO_REUSEADDR} is + * enabled or disabled after a socket is bound (See {@link #isBound()}) * is not defined. * * @param on whether to enable or disable the socket option * @exception SocketException if an error occurs enabling or - * disabling the SO_RESUEADDR socket option, - * or the socket is closed. + * disabling the {@link SocketOptions#SO_REUSEADDR SO_REUSEADDR} + * socket option, or the socket is closed. * @since 1.4 * @see #getReuseAddress() * @see #bind(SocketAddress) @@ -1387,9 +1406,10 @@ class Socket implements java.io.Closeable { } /** - * Tests if SO_REUSEADDR is enabled. + * Tests if {@link SocketOptions#SO_REUSEADDR SO_REUSEADDR} is enabled. * - * @return a boolean indicating whether or not SO_REUSEADDR is enabled. + * @return a boolean indicating whether or not + * {@link SocketOptions#SO_REUSEADDR SO_REUSEADDR} is enabled. * @exception SocketException if there is an error * in the underlying protocol, such as a TCP error. * @since 1.4 diff --git a/jdk/src/share/classes/java/nio/charset/Charset.java b/jdk/src/share/classes/java/nio/charset/Charset.java index 2d3f020bcf6..9451c537a36 100644 --- a/jdk/src/share/classes/java/nio/charset/Charset.java +++ b/jdk/src/share/classes/java/nio/charset/Charset.java @@ -427,46 +427,38 @@ public abstract class Charset } /* The extended set of charsets */ - private static Object extendedProviderLock = new Object(); - private static boolean extendedProviderProbed = false; - private static CharsetProvider extendedProvider = null; - - private static void probeExtendedProvider() { - AccessController.doPrivileged(new PrivilegedAction() { - public Object run() { - try { - Class epc - = Class.forName("sun.nio.cs.ext.ExtendedCharsets"); - extendedProvider = (CharsetProvider)epc.newInstance(); - } catch (ClassNotFoundException x) { - // Extended charsets not available - // (charsets.jar not present) - } catch (InstantiationException x) { - throw new Error(x); - } catch (IllegalAccessException x) { - throw new Error(x); - } - return null; - } - }); + private static class ExtendedProviderHolder { + static final CharsetProvider extendedProvider = extendedProvider(); + // returns ExtendedProvider, if installed + private static CharsetProvider extendedProvider() { + return AccessController.doPrivileged( + new PrivilegedAction() { + public CharsetProvider run() { + try { + Class epc + = Class.forName("sun.nio.cs.ext.ExtendedCharsets"); + return (CharsetProvider)epc.newInstance(); + } catch (ClassNotFoundException x) { + // Extended charsets not available + // (charsets.jar not present) + } catch (InstantiationException | + IllegalAccessException x) { + throw new Error(x); + } + return null; + } + }); + } } private static Charset lookupExtendedCharset(String charsetName) { - CharsetProvider ecp = null; - synchronized (extendedProviderLock) { - if (!extendedProviderProbed) { - probeExtendedProvider(); - extendedProviderProbed = true; - } - ecp = extendedProvider; - } + CharsetProvider ecp = ExtendedProviderHolder.extendedProvider; return (ecp != null) ? ecp.charsetForName(charsetName) : null; } private static Charset lookup(String charsetName) { if (charsetName == null) throw new IllegalArgumentException("Null charset name"); - Object[] a; if ((a = cache1) != null && charsetName.equals(a[0])) return (Charset)a[1]; @@ -483,7 +475,6 @@ public abstract class Charset cache1 = a; return (Charset)a[1]; } - Charset cs; if ((cs = standardProvider.charsetForName(charsetName)) != null || (cs = lookupExtendedCharset(charsetName)) != null || @@ -589,6 +580,9 @@ public abstract class Charset new TreeMap( ASCIICaseInsensitiveComparator.CASE_INSENSITIVE_ORDER); put(standardProvider.charsets(), m); + CharsetProvider ecp = ExtendedProviderHolder.extendedProvider; + if (ecp != null) + put(ecp.charsets(), m); for (Iterator i = providers(); i.hasNext();) { CharsetProvider cp = i.next(); put(cp.charsets(), m); diff --git a/jdk/src/share/classes/java/time/Clock.java b/jdk/src/share/classes/java/time/Clock.java index c0be4b0e42e..cd90822925f 100644 --- a/jdk/src/share/classes/java/time/Clock.java +++ b/jdk/src/share/classes/java/time/Clock.java @@ -103,7 +103,7 @@ import java.util.TimeZone; * system clock This may use {@link System#currentTimeMillis()}, or a higher * resolution clock if one is available. * - *

      Specification for implementors

      + * @implSpec * This abstract class must be implemented with care to ensure other operate correctly. * All implementations that can be instantiated must be final, immutable and thread-safe. *

      @@ -112,13 +112,23 @@ import java.util.TimeZone; * obtain the time from a central time server across the network. Obviously, in this case the * lookup could fail, and so the method is permitted to throw an exception. *

      - * The returned instants from {@code Clock} work on a time-scale that ignores leap seconds. - * If the implementation wraps a source that provides leap second information, then a mechanism - * should be used to "smooth" the leap second, such as UTC-SLS. + * The returned instants from {@code Clock} work on a time-scale that ignores leap seconds, + * as described in {@link Instant}. If the implementation wraps a source that provides leap + * second information, then a mechanism should be used to "smooth" the leap second. + * The Java Time-Scale mandates the use of UTC-SLS, however clock implementations may choose + * how accurate they are with the time-scale so long as they document how they work. + * Implementations are therefore not required to actually perform the UTC-SLS slew or to + * otherwise be aware of leap seconds. *

      * Implementations should implement {@code Serializable} wherever possible and must * document whether or not they do support serialization. * + * @implNote + * The clock implementation provided here is based on {@link System#currentTimeMillis()}. + * That method provides little to no guarantee about the accuracy of the clock. + * Applications requiring a more accurate clock must implement this abstract class + * themselves using a different external clock, such as an NTP server. + * * @since 1.8 */ public abstract class Clock { @@ -370,7 +380,7 @@ public abstract class Clock { /** * Gets the current millisecond instant of the clock. *

      - * This returns the millisecond-based instant, measured from 1970-01-01T00:00 UTC. + * This returns the millisecond-based instant, measured from 1970-01-01T00:00Z (UTC). * This is equivalent to the definition of {@link System#currentTimeMillis()}. *

      * Most applications should avoid this method and use {@link Instant} to represent @@ -381,7 +391,7 @@ public abstract class Clock { * The default implementation currently calls {@link #instant}. * * @return the current millisecond instant from this clock, measured from - * the Java epoch of 1970-01-01T00:00 UTC, not null + * the Java epoch of 1970-01-01T00:00Z (UTC), not null * @throws DateTimeException if the instant cannot be obtained, not thrown by most implementations */ public long millis() { diff --git a/jdk/src/share/classes/java/time/DateTimeException.java b/jdk/src/share/classes/java/time/DateTimeException.java index e5e9efa85ca..c2549fe0a32 100644 --- a/jdk/src/share/classes/java/time/DateTimeException.java +++ b/jdk/src/share/classes/java/time/DateTimeException.java @@ -67,7 +67,7 @@ package java.time; * This exception is used to indicate problems with creating, querying * and manipulating date-time objects. * - *

      Specification for implementors

      + * @implSpec * This class is intended for use in a single thread. * * @since 1.8 diff --git a/jdk/src/share/classes/java/time/DayOfWeek.java b/jdk/src/share/classes/java/time/DayOfWeek.java index 767058ba328..d2a6f5aa541 100644 --- a/jdk/src/share/classes/java/time/DayOfWeek.java +++ b/jdk/src/share/classes/java/time/DayOfWeek.java @@ -100,7 +100,7 @@ import java.util.Locale; * As such, this enum may be used by any calendar system that has the day-of-week * concept defined exactly equivalent to the ISO calendar system. * - *

      Specification for implementors

      + * @implSpec * This is an immutable and thread-safe enum. * * @since 1.8 diff --git a/jdk/src/share/classes/java/time/Duration.java b/jdk/src/share/classes/java/time/Duration.java index 73c04fde27e..52b225e6e69 100644 --- a/jdk/src/share/classes/java/time/Duration.java +++ b/jdk/src/share/classes/java/time/Duration.java @@ -118,7 +118,7 @@ import java.util.regex.Pattern; * most applications. * See {@link Instant} for a discussion as to the meaning of the second and time-scales. * - *

      Specification for implementors

      + * @implSpec * This class is immutable and thread-safe. * * @since 1.8 diff --git a/jdk/src/share/classes/java/time/Instant.java b/jdk/src/share/classes/java/time/Instant.java index bacdba0f498..6dc24646945 100644 --- a/jdk/src/share/classes/java/time/Instant.java +++ b/jdk/src/share/classes/java/time/Instant.java @@ -142,47 +142,60 @@ import java.util.Objects; * introduce other changes. *

      * Given the complexity of accurate timekeeping described above, this Java API defines - * its own time-scale with a simplification. The Java time-scale is defined as follows: + * its own time-scale, the Java Time-Scale. + *

      + * The Java Time-Scale divides each calendar day into exactly 86400 + * subdivisions, known as seconds. These seconds may differ from the + * SI second. It closely matches the de facto international civil time + * scale, the definition of which changes from time to time. + *

      + * The Java Time-Scale has slightly different definitions for different + * segments of the time-line, each based on the consensus international + * time scale that is used as the basis for civil time. Whenever the + * internationally-agreed time scale is modified or replaced, a new + * segment of the Java Time-Scale must be defined for it. Each segment + * must meet these requirements: *

        - *
      • midday will always be exactly as defined by the agreed international civil time
      • - *
      • other times during the day will be broadly in line with the agreed international civil time
      • - *
      • the day will be divided into exactly 86400 subdivisions, referred to as "seconds"
      • - *
      • the Java "second" may differ from an SI second
      • - *
      • a well-defined algorithm must be specified to map each second in the accurate agreed - * international civil time to each "second" in this time-scale
      • + *
      • the Java Time-Scale shall closely match the underlying international + * civil time scale;
      • + *
      • the Java Time-Scale shall exactly match the international civil + * time scale at noon each day;
      • + *
      • the Java Time-Scale shall have a precisely-defined relationship to + * the international civil time scale.
      • *

      - * Agreed international civil time is the base time-scale agreed by international convention, - * which in 2012 is UTC (with leap-seconds). + * There are currently, as of 2013, two segments in the Java time-scale. *

      - * In 2012, the definition of the Java time-scale is the same as UTC for all days except - * those where a leap-second occurs. On days where a leap-second does occur, the time-scale - * effectively eliminates the leap-second, maintaining the fiction of 86400 seconds in the day. - * The approved well-defined algorithm to eliminate leap-seconds is specified as + * For the segment from 1972-11-03 (exact boundary discussed below) until + * further notice, the consensus international time scale is UTC (with + * leap seconds). In this segment, the Java Time-Scale is identical to * UTC-SLS. + * This is identical to UTC on days that do not have a leap second. + * On days that do have a leap second, the leap second is spread equally + * over the last 1000 seconds of the day, maintaining the appearance of + * exactly 86400 seconds per day. *

      - * UTC-SLS is a simple algorithm that smoothes the leap-second over the last 1000 seconds of - * the day, making each of the last 1000 seconds 1/1000th longer or shorter than an SI second. - * Implementations built on an accurate leap-second aware time source should use UTC-SLS. - * Use of a different algorithm risks confusion and misinterpretation of instants around a - * leap-second and is discouraged. + * For the segment prior to 1972-11-03, extending back arbitrarily far, + * the consensus international time scale is defined to be UT1, applied + * proleptically, which is equivalent to the (mean) solar time on the + * prime meridian (Greenwich). In this segment, the Java Time-Scale is + * identical to the consensus international time scale. The exact + * boundary between the two segments is the instant where UT1 = UTC + * between 1972-11-03T00:00 and 1972-11-04T12:00. *

      - * The main benefit of always dividing the day into 86400 subdivisions is that it matches the - * expectations of most users of the API. The alternative is to force every user to understand - * what a leap second is and to force them to have special logic to handle them. - * Most applications do not have access to a clock that is accurate enough to record leap-seconds. - * Most applications also do not have a problem with a second being a very small amount longer or - * shorter than a real SI second during a leap-second. - *

      - * One final problem is the definition of the agreed international civil time before the - * introduction of modern UTC in 1972. This includes the Java epoch of {@code 1970-01-01}. - * It is intended that instants before 1972 be interpreted based on the solar day divided - * into 86400 subdivisions, as per the principles of UT1. + * Implementations of the Java time-scale using the JSR-310 API are not + * required to provide any clock that is sub-second accurate, or that + * progresses monotonically or smoothly. Implementations are therefore + * not required to actually perform the UTC-SLS slew or to otherwise be + * aware of leap seconds. JSR-310 does, however, require that + * implementations must document the approach they use when defining a + * clock representing the current instant. + * See {@link Clock} for details on the available clocks. *

      * The Java time-scale is used for all date-time classes. * This includes {@code Instant}, {@code LocalDate}, {@code LocalTime}, {@code OffsetDateTime}, * {@code ZonedDateTime} and {@code Duration}. * - *

      Specification for implementors

      + * @implSpec * This class is immutable and thread-safe. * * @since 1.8 @@ -1030,16 +1043,16 @@ public final class Instant } /** - * Calculates the period between this instant and another instant in - * terms of the specified unit. + * Calculates the amount of time until another instant in terms of the specified unit. *

      - * This calculates the period between two instants in terms of a single unit. + * This calculates the amount of time between two {@code Instant} + * objects in terms of a single {@code TemporalUnit}. * The start and end points are {@code this} and the specified instant. * The result will be negative if the end is before the start. * The calculation returns a whole number, representing the number of * complete units between the two instants. * The {@code Temporal} passed to this method must be an {@code Instant}. - * For example, the period in days between two dates can be calculated + * For example, the amount in days between two dates can be calculated * using {@code startInstant.periodUntil(endInstant, SECONDS)}. *

      * There are two equivalent ways of using this method. @@ -1064,10 +1077,10 @@ public final class Instant *

      * This instance is immutable and unaffected by this method call. * - * @param endInstant the end date, which must be a {@code LocalDate}, not null - * @param unit the unit to measure the period in, not null - * @return the amount of the period between this date and the end date - * @throws DateTimeException if the period cannot be calculated + * @param endInstant the end date, which must be an {@code Instant}, not null + * @param unit the unit to measure the amount in, not null + * @return the amount of time between this instant and the end instant + * @throws DateTimeException if the amount cannot be calculated * @throws UnsupportedTemporalTypeException if the unit is not supported * @throws ArithmeticException if numeric overflow occurs */ @@ -1075,7 +1088,7 @@ public final class Instant public long periodUntil(Temporal endInstant, TemporalUnit unit) { if (endInstant instanceof Instant == false) { Objects.requireNonNull(endInstant, "endInstant"); - throw new DateTimeException("Unable to calculate period between objects of two different types"); + throw new DateTimeException("Unable to calculate amount as objects are of two different types"); } Instant end = (Instant) endInstant; if (unit instanceof ChronoUnit) { diff --git a/jdk/src/share/classes/java/time/LocalDate.java b/jdk/src/share/classes/java/time/LocalDate.java index cb1219bdb81..ede6d8326b9 100644 --- a/jdk/src/share/classes/java/time/LocalDate.java +++ b/jdk/src/share/classes/java/time/LocalDate.java @@ -121,7 +121,7 @@ import java.util.Objects; * However, any application that makes use of historical dates, and requires them * to be accurate will find the ISO-8601 approach unsuitable. * - *

      Specification for implementors

      + * @implSpec * This class is immutable and thread-safe. * * @since 1.8 @@ -1489,19 +1489,19 @@ public final class LocalDate } /** - * Calculates the period between this date and another date in - * terms of the specified unit. + * Calculates the amount of time until another date in terms of the specified unit. *

      - * This calculates the period between two dates in terms of a single unit. + * This calculates the amount of time between two {@code LocalDate} + * objects in terms of a single {@code TemporalUnit}. * The start and end points are {@code this} and the specified date. * The result will be negative if the end is before the start. * The {@code Temporal} passed to this method must be a {@code LocalDate}. - * For example, the period in days between two dates can be calculated + * For example, the amount in days between two dates can be calculated * using {@code startDate.periodUntil(endDate, DAYS)}. *

      * The calculation returns a whole number, representing the number of * complete units between the two dates. - * For example, the period in months between 2012-06-15 and 2012-08-14 + * For example, the amount in months between 2012-06-15 and 2012-08-14 * will only be one month as it is one day short of two months. *

      * There are two equivalent ways of using this method. @@ -1527,9 +1527,9 @@ public final class LocalDate * This instance is immutable and unaffected by this method call. * * @param endDate the end date, which must be a {@code LocalDate}, not null - * @param unit the unit to measure the period in, not null - * @return the amount of the period between this date and the end date - * @throws DateTimeException if the period cannot be calculated + * @param unit the unit to measure the amount in, not null + * @return the amount of time between this date and the end date + * @throws DateTimeException if the amount cannot be calculated * @throws UnsupportedTemporalTypeException if the unit is not supported * @throws ArithmeticException if numeric overflow occurs */ @@ -1538,7 +1538,7 @@ public final class LocalDate Objects.requireNonNull(unit, "unit"); if (endDate instanceof LocalDate == false) { Objects.requireNonNull(endDate, "endDate"); - throw new DateTimeException("Unable to calculate period between objects of two different types"); + throw new DateTimeException("Unable to calculate amount as objects are of two different types"); } LocalDate end = (LocalDate) endDate; if (unit instanceof ChronoUnit) { diff --git a/jdk/src/share/classes/java/time/LocalDateTime.java b/jdk/src/share/classes/java/time/LocalDateTime.java index 2921d399edd..6e6c87bc2ff 100644 --- a/jdk/src/share/classes/java/time/LocalDateTime.java +++ b/jdk/src/share/classes/java/time/LocalDateTime.java @@ -119,7 +119,7 @@ import java.util.Objects; * However, any application that makes use of historical dates, and requires them * to be accurate will find the ISO-8601 approach unsuitable. * - *

      Specification for implementors

      + * @implSpec * This class is immutable and thread-safe. * * @since 1.8 @@ -1562,19 +1562,19 @@ public final class LocalDateTime } /** - * Calculates the period between this date-time and another date-time in - * terms of the specified unit. + * Calculates the amount of time until another date-time in terms of the specified unit. *

      - * This calculates the period between two date-times in terms of a single unit. + * This calculates the amount of time between two {@code LocalDateTime} + * objects in terms of a single {@code TemporalUnit}. * The start and end points are {@code this} and the specified date-time. * The result will be negative if the end is before the start. * The {@code Temporal} passed to this method must be a {@code LocalDateTime}. - * For example, the period in days between two date-times can be calculated + * For example, the amount in days between two date-times can be calculated * using {@code startDateTime.periodUntil(endDateTime, DAYS)}. *

      * The calculation returns a whole number, representing the number of * complete units between the two date-times. - * For example, the period in months between 2012-06-15T00:00 and 2012-08-14T23:59 + * For example, the amount in months between 2012-06-15T00:00 and 2012-08-14T23:59 * will only be one month as it is one minute short of two months. *

      * There are two equivalent ways of using this method. @@ -1602,9 +1602,9 @@ public final class LocalDateTime * This instance is immutable and unaffected by this method call. * * @param endDateTime the end date-time, which must be a {@code LocalDateTime}, not null - * @param unit the unit to measure the period in, not null - * @return the amount of the period between this date-time and the end date-time - * @throws DateTimeException if the period cannot be calculated + * @param unit the unit to measure the amount in, not null + * @return the amount of time between this date-time and the end date-time + * @throws DateTimeException if the amount cannot be calculated * @throws UnsupportedTemporalTypeException if the unit is not supported * @throws ArithmeticException if numeric overflow occurs */ @@ -1612,7 +1612,7 @@ public final class LocalDateTime public long periodUntil(Temporal endDateTime, TemporalUnit unit) { if (endDateTime instanceof LocalDateTime == false) { Objects.requireNonNull(endDateTime, "endDateTime"); - throw new DateTimeException("Unable to calculate period between objects of two different types"); + throw new DateTimeException("Unable to calculate amount as objects are of two different types"); } LocalDateTime end = (LocalDateTime) endDateTime; if (unit instanceof ChronoUnit) { diff --git a/jdk/src/share/classes/java/time/LocalTime.java b/jdk/src/share/classes/java/time/LocalTime.java index 1e909f891b0..41b1a927560 100644 --- a/jdk/src/share/classes/java/time/LocalTime.java +++ b/jdk/src/share/classes/java/time/LocalTime.java @@ -109,7 +109,7 @@ import java.util.Objects; * in most of the world. This API assumes that all calendar systems use the same * representation, this class, for time-of-day. * - *

      Specification for implementors

      + * @implSpec * This class is immutable and thread-safe. * * @since 1.8 @@ -974,9 +974,6 @@ public final class LocalTime * Returns a {@code LocalTime} with the specified number of half-days added. * This is equivalent to {@link #plusHours(long)} with the amount * multiplied by 12. - *
    2. {@code DAYS} - - * Returns a {@code LocalTime} with the specified number of days added. - * This returns {@code this} time. * *

      * All other {@code ChronoUnit} instances will throw an {@code UnsupportedTemporalTypeException}. @@ -1007,7 +1004,6 @@ public final class LocalTime case MINUTES: return plusMinutes(amountToAdd); case HOURS: return plusHours(amountToAdd); case HALF_DAYS: return plusHours((amountToAdd % 2) * 12); - case DAYS: return this; } throw new UnsupportedTemporalTypeException("Unsupported unit: " + unit.getName()); } @@ -1291,19 +1287,19 @@ public final class LocalTime } /** - * Calculates the period between this time and another time in - * terms of the specified unit. + * Calculates the amount of time until another time in terms of the specified unit. *

      - * This calculates the period between two times in terms of a single unit. + * This calculates the amount of time between two {@code LocalTime} + * objects in terms of a single {@code TemporalUnit}. * The start and end points are {@code this} and the specified time. * The result will be negative if the end is before the start. * The {@code Temporal} passed to this method must be a {@code LocalTime}. - * For example, the period in hours between two times can be calculated + * For example, the amount in hours between two times can be calculated * using {@code startTime.periodUntil(endTime, HOURS)}. *

      * The calculation returns a whole number, representing the number of * complete units between the two times. - * For example, the period in hours between 11:30 and 13:29 will only + * For example, the amount in hours between 11:30 and 13:29 will only * be one hour as it is one minute short of two hours. *

      * There are two equivalent ways of using this method. @@ -1329,9 +1325,9 @@ public final class LocalTime * This instance is immutable and unaffected by this method call. * * @param endTime the end time, which must be a {@code LocalTime}, not null - * @param unit the unit to measure the period in, not null - * @return the amount of the period between this time and the end time - * @throws DateTimeException if the period cannot be calculated + * @param unit the unit to measure the amount in, not null + * @return the amount of time between this time and the end time + * @throws DateTimeException if the amount cannot be calculated * @throws UnsupportedTemporalTypeException if the unit is not supported * @throws ArithmeticException if numeric overflow occurs */ @@ -1339,7 +1335,7 @@ public final class LocalTime public long periodUntil(Temporal endTime, TemporalUnit unit) { if (endTime instanceof LocalTime == false) { Objects.requireNonNull(endTime, "endTime"); - throw new DateTimeException("Unable to calculate period between objects of two different types"); + throw new DateTimeException("Unable to calculate amount as objects are of two different types"); } LocalTime end = (LocalTime) endTime; if (unit instanceof ChronoUnit) { diff --git a/jdk/src/share/classes/java/time/Month.java b/jdk/src/share/classes/java/time/Month.java index 175d0517e2b..272e8f96b36 100644 --- a/jdk/src/share/classes/java/time/Month.java +++ b/jdk/src/share/classes/java/time/Month.java @@ -97,7 +97,7 @@ import java.util.Locale; * As such, this enum may be used by any calendar system that has the month-of-year * concept defined exactly equivalent to the ISO-8601 calendar system. * - *

      Specification for implementors

      + * @implSpec * This is an immutable and thread-safe enum. * * @since 1.8 diff --git a/jdk/src/share/classes/java/time/MonthDay.java b/jdk/src/share/classes/java/time/MonthDay.java index 276e869d10e..4204ef74a7d 100644 --- a/jdk/src/share/classes/java/time/MonthDay.java +++ b/jdk/src/share/classes/java/time/MonthDay.java @@ -111,7 +111,7 @@ import java.util.Objects; * However, any application that makes use of historical dates, and requires them * to be accurate will find the ISO-8601 approach unsuitable. * - *

      Specification for implementors

      + * @implSpec * This class is immutable and thread-safe. * * @since 1.8 diff --git a/jdk/src/share/classes/java/time/OffsetDateTime.java b/jdk/src/share/classes/java/time/OffsetDateTime.java index aa4c7281c95..b053085a9f5 100644 --- a/jdk/src/share/classes/java/time/OffsetDateTime.java +++ b/jdk/src/share/classes/java/time/OffsetDateTime.java @@ -111,7 +111,7 @@ import java.util.Objects; * in simpler applications. This class may be used when modeling date-time concepts in * more detail, or when communicating to a database or in a network protocol. * - *

      Specification for implementors

      + * @implSpec * This class is immutable and thread-safe. * * @since 1.8 @@ -1521,10 +1521,10 @@ public final class OffsetDateTime } /** - * Calculates the period between this date-time and another date-time in - * terms of the specified unit. + * Calculates the amount of time until another date-time in terms of the specified unit. *

      - * This calculates the period between two date-times in terms of a single unit. + * This calculates the amount of time between two {@code OffsetDateTime} + * objects in terms of a single {@code TemporalUnit}. * The start and end points are {@code this} and the specified date-time. * The result will be negative if the end is before the start. * For example, the period in days between two date-times can be calculated @@ -1564,9 +1564,9 @@ public final class OffsetDateTime * This instance is immutable and unaffected by this method call. * * @param endDateTime the end date-time, which must be an {@code OffsetDateTime}, not null - * @param unit the unit to measure the period in, not null - * @return the amount of the period between this date-time and the end date-time - * @throws DateTimeException if the period cannot be calculated + * @param unit the unit to measure the amount in, not null + * @return the amount of time between this date-time and the end date-time + * @throws DateTimeException if the amount cannot be calculated * @throws UnsupportedTemporalTypeException if the unit is not supported * @throws ArithmeticException if numeric overflow occurs */ @@ -1574,7 +1574,7 @@ public final class OffsetDateTime public long periodUntil(Temporal endDateTime, TemporalUnit unit) { if (endDateTime instanceof OffsetDateTime == false) { Objects.requireNonNull(endDateTime, "endDateTime"); - throw new DateTimeException("Unable to calculate period between objects of two different types"); + throw new DateTimeException("Unable to calculate amount as objects are of two different types"); } if (unit instanceof ChronoUnit) { OffsetDateTime end = (OffsetDateTime) endDateTime; diff --git a/jdk/src/share/classes/java/time/OffsetTime.java b/jdk/src/share/classes/java/time/OffsetTime.java index c6afcb26e49..ff990696942 100644 --- a/jdk/src/share/classes/java/time/OffsetTime.java +++ b/jdk/src/share/classes/java/time/OffsetTime.java @@ -102,7 +102,7 @@ import java.util.Objects; * For example, the value "13:45.30.123456789+02:00" can be stored * in an {@code OffsetTime}. * - *

      Specification for implementors

      + * @implSpec * This class is immutable and thread-safe. * * @since 1.8 @@ -1077,10 +1077,10 @@ public final class OffsetTime } /** - * Calculates the period between this time and another time in - * terms of the specified unit. + * Calculates the amount of time until another time in terms of the specified unit. *

      - * This calculates the period between two times in terms of a single unit. + * This calculates the amount of time between two {@code OffsetTime} + * objects in terms of a single {@code TemporalUnit}. * The start and end points are {@code this} and the specified time. * The result will be negative if the end is before the start. * For example, the period in hours between two times can be calculated @@ -1118,9 +1118,9 @@ public final class OffsetTime * This instance is immutable and unaffected by this method call. * * @param endTime the end time, which must be an {@code OffsetTime}, not null - * @param unit the unit to measure the period in, not null - * @return the amount of the period between this time and the end time - * @throws DateTimeException if the period cannot be calculated + * @param unit the unit to measure the amount in, not null + * @return the amount of time between this time and the end time + * @throws DateTimeException if the amount cannot be calculated * @throws UnsupportedTemporalTypeException if the unit is not supported * @throws ArithmeticException if numeric overflow occurs */ @@ -1128,7 +1128,7 @@ public final class OffsetTime public long periodUntil(Temporal endTime, TemporalUnit unit) { if (endTime instanceof OffsetTime == false) { Objects.requireNonNull(endTime, "endTime"); - throw new DateTimeException("Unable to calculate period between objects of two different types"); + throw new DateTimeException("Unable to calculate amount as objects are of two different types"); } if (unit instanceof ChronoUnit) { OffsetTime end = (OffsetTime) endTime; diff --git a/jdk/src/share/classes/java/time/Period.java b/jdk/src/share/classes/java/time/Period.java index 85980d9d874..b2748f089ce 100644 --- a/jdk/src/share/classes/java/time/Period.java +++ b/jdk/src/share/classes/java/time/Period.java @@ -119,7 +119,7 @@ import java.util.regex.Pattern; * The months and years fields may be {@linkplain #normalized() normalized}. * The normalization assumes a 12 month year, so is not appropriate for all calendar systems. * - *

      Specification for implementors

      + * @implSpec * This class is immutable and thread-safe. * * @since 1.8 diff --git a/jdk/src/share/classes/java/time/Ser.java b/jdk/src/share/classes/java/time/Ser.java index 401a7cd832a..607198952c1 100644 --- a/jdk/src/share/classes/java/time/Ser.java +++ b/jdk/src/share/classes/java/time/Ser.java @@ -66,7 +66,7 @@ import java.io.StreamCorruptedException; /** * The shared serialization delegate for this package. * - *

      Implementation notes

      + * @implNote * This class wraps the object being serialized, and takes a byte representing the type of the class to * be serialized. This byte can also be used for versioning the serialization format. In this case another * byte flag would be used in order to specify an alternative version of the type format. diff --git a/jdk/src/share/classes/java/time/Year.java b/jdk/src/share/classes/java/time/Year.java index 2ef92b53885..c2d9974d79a 100644 --- a/jdk/src/share/classes/java/time/Year.java +++ b/jdk/src/share/classes/java/time/Year.java @@ -115,7 +115,7 @@ import java.util.Objects; * However, any application that makes use of historical dates, and requires them * to be accurate will find the ISO-8601 approach unsuitable. * - *

      Specification for implementors

      + * @implSpec * This class is immutable and thread-safe. * * @since 1.8 @@ -813,10 +813,10 @@ public final class Year } /** - * Calculates the period between this year and another year in - * terms of the specified unit. + * Calculates the amount of time until another year in terms of the specified unit. *

      - * This calculates the period between two years in terms of a single unit. + * This calculates the amount of time between two {@code Year} + * objects in terms of a single {@code TemporalUnit}. * The start and end points are {@code this} and the specified year. * The result will be negative if the end is before the start. * The {@code Temporal} passed to this method must be a {@code Year}. @@ -851,9 +851,9 @@ public final class Year * This instance is immutable and unaffected by this method call. * * @param endYear the end year, which must be a {@code Year}, not null - * @param unit the unit to measure the period in, not null - * @return the amount of the period between this year and the end year - * @throws DateTimeException if the period cannot be calculated + * @param unit the unit to measure the amount in, not null + * @return the amount of time between this year and the end year + * @throws DateTimeException if the amount cannot be calculated * @throws UnsupportedTemporalTypeException if the unit is not supported * @throws ArithmeticException if numeric overflow occurs */ @@ -861,7 +861,7 @@ public final class Year public long periodUntil(Temporal endYear, TemporalUnit unit) { if (endYear instanceof Year == false) { Objects.requireNonNull(endYear, "endYear"); - throw new DateTimeException("Unable to calculate period between objects of two different types"); + throw new DateTimeException("Unable to calculate amount as objects are of two different types"); } Year end = (Year) endYear; if (unit instanceof ChronoUnit) { diff --git a/jdk/src/share/classes/java/time/YearMonth.java b/jdk/src/share/classes/java/time/YearMonth.java index 312a36cd3f7..855774eed1a 100644 --- a/jdk/src/share/classes/java/time/YearMonth.java +++ b/jdk/src/share/classes/java/time/YearMonth.java @@ -110,7 +110,7 @@ import java.util.Objects; * However, any application that makes use of historical dates, and requires them * to be accurate will find the ISO-8601 approach unsuitable. * - *

      Specification for implementors

      + * @implSpec * This class is immutable and thread-safe. * * @since 1.8 @@ -944,10 +944,10 @@ public final class YearMonth } /** - * Calculates the period between this year-month and another year-month in - * terms of the specified unit. + * Calculates the amount of time until another year-month in terms of the specified unit. *

      - * This calculates the period between two year-months in terms of a single unit. + * This calculates the amount of time between two {@code YearMonth} + * objects in terms of a single {@code TemporalUnit}. * The start and end points are {@code this} and the specified year-month. * The result will be negative if the end is before the start. * The {@code Temporal} passed to this method must be a {@code YearMonth}. @@ -982,9 +982,9 @@ public final class YearMonth * This instance is immutable and unaffected by this method call. * * @param endYearMonth the end year-month, which must be a {@code YearMonth}, not null - * @param unit the unit to measure the period in, not null - * @return the amount of the period between this year-month and the end year-month - * @throws DateTimeException if the period cannot be calculated + * @param unit the unit to measure the amount in, not null + * @return the amount of time between this year-month and the end year-month + * @throws DateTimeException if the amount cannot be calculated * @throws UnsupportedTemporalTypeException if the unit is not supported * @throws ArithmeticException if numeric overflow occurs */ @@ -992,7 +992,7 @@ public final class YearMonth public long periodUntil(Temporal endYearMonth, TemporalUnit unit) { if (endYearMonth instanceof YearMonth == false) { Objects.requireNonNull(endYearMonth, "endYearMonth"); - throw new DateTimeException("Unable to calculate period between objects of two different types"); + throw new DateTimeException("Unable to calculate amount as objects are of two different types"); } YearMonth end = (YearMonth) endYearMonth; if (unit instanceof ChronoUnit) { diff --git a/jdk/src/share/classes/java/time/ZoneId.java b/jdk/src/share/classes/java/time/ZoneId.java index 38a2a7ca289..026f73ef512 100644 --- a/jdk/src/share/classes/java/time/ZoneId.java +++ b/jdk/src/share/classes/java/time/ZoneId.java @@ -157,7 +157,7 @@ import java.util.TimeZone; * This approach is designed to allow a {@link ZonedDateTime} to be loaded and * queried, but not modified, on a Java Runtime with incomplete time-zone information. * - *

      Specification for implementors

      + * @implSpec * This abstract class has two implementations, both of which are immutable and thread-safe. * One implementation models region-based IDs, the other is {@code ZoneOffset} modelling * offset-based IDs. This difference is visible in serialization. diff --git a/jdk/src/share/classes/java/time/ZoneOffset.java b/jdk/src/share/classes/java/time/ZoneOffset.java index 00f5dce485f..4bbc4e4fa84 100644 --- a/jdk/src/share/classes/java/time/ZoneOffset.java +++ b/jdk/src/share/classes/java/time/ZoneOffset.java @@ -114,7 +114,7 @@ import java.util.concurrent.ConcurrentMap; * Implementations may choose to cache certain common offsets, however * applications must not rely on such caching. * - *

      Specification for implementors

      + * @implSpec * This class is immutable and thread-safe. * * @since 1.8 diff --git a/jdk/src/share/classes/java/time/ZoneRegion.java b/jdk/src/share/classes/java/time/ZoneRegion.java index 912ee0499d3..af6a5405a69 100644 --- a/jdk/src/share/classes/java/time/ZoneRegion.java +++ b/jdk/src/share/classes/java/time/ZoneRegion.java @@ -83,7 +83,7 @@ import java.util.regex.Pattern; * By contrast, the region identifier is well-defined and long-lived. * This separation also allows rules to be shared between regions if appropriate. * - *

      Specification for implementors

      + * @implSpec * This class is immutable and thread-safe. * * @since 1.8 diff --git a/jdk/src/share/classes/java/time/ZonedDateTime.java b/jdk/src/share/classes/java/time/ZonedDateTime.java index f5115c66873..e7ed5551d33 100644 --- a/jdk/src/share/classes/java/time/ZonedDateTime.java +++ b/jdk/src/share/classes/java/time/ZonedDateTime.java @@ -142,7 +142,7 @@ import java.util.Objects; * a vital, but secondary, piece of information, used to ensure that the class * represents an instant, especially during a daylight savings overlap. * - *

      Specification for implementors

      + * @implSpec * A {@code ZonedDateTime} holds state equivalent to three separate objects, * a {@code LocalDateTime}, a {@code ZoneId} and the resolved {@code ZoneOffset}. * The offset and local date-time are used to define an instant when necessary. @@ -1983,10 +1983,10 @@ public final class ZonedDateTime } /** - * Calculates the period between this date-time and another date-time in - * terms of the specified unit. + * Calculates the amount of time until another date-time in terms of the specified unit. *

      - * This calculates the period between two date-times in terms of a single unit. + * This calculates the amount of time between two {@code ZonedDateTime} + * objects in terms of a single {@code TemporalUnit}. * The start and end points are {@code this} and the specified date-time. * The result will be negative if the end is before the start. * For example, the period in days between two date-times can be calculated @@ -2040,9 +2040,9 @@ public final class ZonedDateTime * This instance is immutable and unaffected by this method call. * * @param endDateTime the end date-time, which must be a {@code ZonedDateTime}, not null - * @param unit the unit to measure the period in, not null - * @return the amount of the period between this date-time and the end date-time - * @throws DateTimeException if the period cannot be calculated + * @param unit the unit to measure the amount in, not null + * @return the amount of time between this date-time and the end date-time + * @throws DateTimeException if the amount cannot be calculated * @throws UnsupportedTemporalTypeException if the unit is not supported * @throws ArithmeticException if numeric overflow occurs */ @@ -2050,7 +2050,7 @@ public final class ZonedDateTime public long periodUntil(Temporal endDateTime, TemporalUnit unit) { if (endDateTime instanceof ZonedDateTime == false) { Objects.requireNonNull(endDateTime, "endDateTime"); - throw new DateTimeException("Unable to calculate period between objects of two different types"); + throw new DateTimeException("Unable to calculate amount as objects are of two different types"); } if (unit instanceof ChronoUnit) { ZonedDateTime end = (ZonedDateTime) endDateTime; diff --git a/jdk/src/share/classes/java/time/chrono/ChronoDateImpl.java b/jdk/src/share/classes/java/time/chrono/ChronoDateImpl.java index 26203faa0af..14e2bf58f2a 100644 --- a/jdk/src/share/classes/java/time/chrono/ChronoDateImpl.java +++ b/jdk/src/share/classes/java/time/chrono/ChronoDateImpl.java @@ -130,7 +130,7 @@ import java.util.Objects; * The subclass must function according to the {@code Chronology} class description and must provide its * {@link java.time.chrono.Chronology#getId() chronlogy ID} and {@link Chronology#getCalendarType() calendar type}.

      * - *

      Specification for implementors

      + * @implSpec * This abstract class must be implemented with care to ensure other classes operate correctly. * All implementations that can be instantiated must be final, immutable and thread-safe. * Subclasses should be Serializable wherever possible. @@ -325,11 +325,11 @@ abstract class ChronoDateImpl> Objects.requireNonNull(endDateTime, "endDateTime"); Objects.requireNonNull(unit, "unit"); if (endDateTime instanceof ChronoLocalDate == false) { - throw new DateTimeException("Unable to calculate period between objects of two different types"); + throw new DateTimeException("Unable to calculate amount as objects are of two different types"); } ChronoLocalDate end = (ChronoLocalDate) endDateTime; if (getChronology().equals(end.getChronology()) == false) { - throw new DateTimeException("Unable to calculate period between two different chronologies"); + throw new DateTimeException("Unable to calculate amount as objects have different chronologies"); } if (unit instanceof ChronoUnit) { switch ((ChronoUnit) unit) { diff --git a/jdk/src/share/classes/java/time/chrono/ChronoLocalDate.java b/jdk/src/share/classes/java/time/chrono/ChronoLocalDate.java index 4f648c93725..31c08260d6e 100644 --- a/jdk/src/share/classes/java/time/chrono/ChronoLocalDate.java +++ b/jdk/src/share/classes/java/time/chrono/ChronoLocalDate.java @@ -234,7 +234,7 @@ import java.util.Objects; * Use {@link TemporalAccessor} if read-only access is required, or use {@link Temporal} * if read-write access is required. * - *

      Specification for implementors

      + * @implSpec * This interface must be implemented with care to ensure other classes operate correctly. * All implementations that can be instantiated must be final, immutable and thread-safe. * Subclasses should be Serializable wherever possible. @@ -257,6 +257,7 @@ public interface ChronoLocalDate> * This allows dates in different calendar systems to be compared based * on the position of the date on the local time-line. * The underlying comparison is equivalent to comparing the epoch-day. + * @return a comparator that compares in time-line order ignoring the chronology * * @see #isAfter * @see #isBefore @@ -510,17 +511,17 @@ public interface ChronoLocalDate> } /** - * Calculates the period between this date and another date in - * terms of the specified unit. + * Calculates the amount of time until another date in terms of the specified unit. *

      - * This calculates the period between two dates in terms of a single unit. + * This calculates the amount of time between two {@code ChronoLocalDate} + * objects in terms of a single {@code TemporalUnit}. * The start and end points are {@code this} and the specified date. * The result will be negative if the end is before the start. * The {@code Temporal} passed to this method must be a * {@code ChronoLocalDate} in the same chronology. * The calculation returns a whole number, representing the number of * complete units between the two dates. - * For example, the period in days between two dates can be calculated + * For example, the amount in days between two dates can be calculated * using {@code startDate.periodUntil(endDate, DAYS)}. *

      * There are two equivalent ways of using this method. @@ -548,9 +549,9 @@ public interface ChronoLocalDate> * * @param endDate the end date, which must be a {@code ChronoLocalDate} * in the same chronology, not null - * @param unit the unit to measure the period in, not null - * @return the amount of the period between this date and the end date - * @throws DateTimeException if the period cannot be calculated + * @param unit the unit to measure the amount in, not null + * @return the amount of time between this date and the end date + * @throws DateTimeException if the amount cannot be calculated * @throws ArithmeticException if numeric overflow occurs */ @Override // override for Javadoc diff --git a/jdk/src/share/classes/java/time/chrono/ChronoLocalDateTime.java b/jdk/src/share/classes/java/time/chrono/ChronoLocalDateTime.java index d706db128f3..f0a893a4c95 100644 --- a/jdk/src/share/classes/java/time/chrono/ChronoLocalDateTime.java +++ b/jdk/src/share/classes/java/time/chrono/ChronoLocalDateTime.java @@ -106,7 +106,7 @@ import java.util.Objects; * Ensure that the discussion in {@code ChronoLocalDate} has been read and understood * before using this interface. * - *

      Specification for implementors

      + * @implSpec * This interface must be implemented with care to ensure other classes operate correctly. * All implementations that can be instantiated must be final, immutable and thread-safe. * Subclasses should be Serializable wherever possible. @@ -127,6 +127,8 @@ public interface ChronoLocalDateTime> * on the position of the date-time on the local time-line. * The underlying comparison is equivalent to comparing the epoch-day and nano-of-day. * + * @return a comparator that compares in time-line order ignoring the chronology + * * @see #isAfter * @see #isBefore * @see #isEqual diff --git a/jdk/src/share/classes/java/time/chrono/ChronoLocalDateTimeImpl.java b/jdk/src/share/classes/java/time/chrono/ChronoLocalDateTimeImpl.java index fd6e7c05c46..1f7195bc6f7 100644 --- a/jdk/src/share/classes/java/time/chrono/ChronoLocalDateTimeImpl.java +++ b/jdk/src/share/classes/java/time/chrono/ChronoLocalDateTimeImpl.java @@ -92,7 +92,7 @@ import java.util.Objects; * It does not store or represent a time-zone. For example, the value * "2nd October 2007 at 13:45.30.123456789" can be stored in an {@code ChronoLocalDateTime}. * - *

      Specification for implementors

      + * @implSpec * This class is immutable and thread-safe. * * @param the concrete type for the date of this date-time @@ -353,12 +353,12 @@ final class ChronoLocalDateTimeImpl> @Override public long periodUntil(Temporal endDateTime, TemporalUnit unit) { if (endDateTime instanceof ChronoLocalDateTime == false) { - throw new DateTimeException("Unable to calculate period between objects of two different types"); + throw new DateTimeException("Unable to calculate amount as objects are of two different types"); } @SuppressWarnings("unchecked") ChronoLocalDateTime end = (ChronoLocalDateTime) endDateTime; if (toLocalDate().getChronology().equals(end.toLocalDate().getChronology()) == false) { - throw new DateTimeException("Unable to calculate period between two different chronologies"); + throw new DateTimeException("Unable to calculate amount as objects have different chronologies"); } if (unit instanceof ChronoUnit) { ChronoUnit f = (ChronoUnit) unit; diff --git a/jdk/src/share/classes/java/time/chrono/ChronoZonedDateTime.java b/jdk/src/share/classes/java/time/chrono/ChronoZonedDateTime.java index 4307c104ab4..d838a36ec29 100644 --- a/jdk/src/share/classes/java/time/chrono/ChronoZonedDateTime.java +++ b/jdk/src/share/classes/java/time/chrono/ChronoZonedDateTime.java @@ -107,7 +107,7 @@ import java.util.Objects; * Ensure that the discussion in {@code ChronoLocalDate} has been read and understood * before using this interface. * - *

      Specification for implementors

      + * @implSpec * This interface must be implemented with care to ensure other classes operate correctly. * All implementations that can be instantiated must be final, immutable and thread-safe. * Subclasses should be Serializable wherever possible. @@ -128,6 +128,8 @@ public interface ChronoZonedDateTime> * on the position of the date-time on the instant time-line. * The underlying comparison is equivalent to comparing the epoch-second and nano-of-second. * + * @return a comparator that compares in time-line order ignoring the chronology + * * @see #isAfter * @see #isBefore * @see #isEqual diff --git a/jdk/src/share/classes/java/time/chrono/ChronoZonedDateTimeImpl.java b/jdk/src/share/classes/java/time/chrono/ChronoZonedDateTimeImpl.java index 0061eebdffc..835ec932184 100644 --- a/jdk/src/share/classes/java/time/chrono/ChronoZonedDateTimeImpl.java +++ b/jdk/src/share/classes/java/time/chrono/ChronoZonedDateTimeImpl.java @@ -95,7 +95,7 @@ import java.util.Objects; * the local time-line overlaps, typically as a result of the end of daylight time. * Information about the local-time can be obtained using methods on the time-zone. * - *

      Specification for implementors

      + * @implSpec * This class is immutable and thread-safe. * * @param the concrete type for the date of this date-time @@ -287,12 +287,12 @@ final class ChronoZonedDateTimeImpl> @Override public long periodUntil(Temporal endDateTime, TemporalUnit unit) { if (endDateTime instanceof ChronoZonedDateTime == false) { - throw new DateTimeException("Unable to calculate period between objects of two different types"); + throw new DateTimeException("Unable to calculate amount as objects are of two different types"); } @SuppressWarnings("unchecked") ChronoZonedDateTime end = (ChronoZonedDateTime) endDateTime; if (toLocalDate().getChronology().equals(end.toLocalDate().getChronology()) == false) { - throw new DateTimeException("Unable to calculate period between two different chronologies"); + throw new DateTimeException("Unable to calculate amount as objects have different chronologies"); } if (unit instanceof ChronoUnit) { end = end.withZoneSameInstant(offset); diff --git a/jdk/src/share/classes/java/time/chrono/Chronology.java b/jdk/src/share/classes/java/time/chrono/Chronology.java index 6c61c32e9c0..e5daae76464 100644 --- a/jdk/src/share/classes/java/time/chrono/Chronology.java +++ b/jdk/src/share/classes/java/time/chrono/Chronology.java @@ -176,7 +176,7 @@ import sun.util.logging.PlatformLogger; * CLDR specification then the calendar type is the concatenation of the * CLDR type and, if applicable, the CLDR variant, * - *

      Specification for implementors

      + * @implSpec * This class must be implemented with care to ensure other classes operate correctly. * All implementations that can be instantiated must be final, immutable and thread-safe. * Subclasses should be Serializable wherever possible. @@ -338,16 +338,13 @@ public abstract class Chronology implements Comparable { *

      * The {@code Locale} class also supports an extension mechanism that * can be used to identify a calendar system. The mechanism is a form - * of key-value pairs, where the calendar system has the key "ca" - * and an optional variant key "cv". + * of key-value pairs, where the calendar system has the key "ca". * For example, the locale "en-JP-u-ca-japanese" represents the English * language as used in Japan with the Japanese calendar system. *

      * This method finds the desired calendar system by in a manner equivalent * to passing "ca" to {@link Locale#getUnicodeLocaleType(String)}. * If the "ca" key is not present, then {@code IsoChronology} is returned. - * The variant, if present, is appended to the "ca" value separated by "-" - * and the concatenated value is used to find the calendar system by type. *

      * Note that the behavior of this method differs from the older * {@link java.util.Calendar#getInstance(Locale)} method. @@ -374,10 +371,6 @@ public abstract class Chronology implements Comparable { if (type == null || "iso".equals(type) || "iso8601".equals(type)) { return IsoChronology.INSTANCE; } - String variant = locale.getUnicodeLocaleType("cv"); - if (variant != null && !variant.isEmpty()) { - type = type + '-' + variant; - } // Not pre-defined; lookup by the type do { Chronology chrono = CHRONOS_BY_TYPE.get(type); @@ -563,7 +556,7 @@ public abstract class Chronology implements Comparable { * and the variant, if applicable, is appended separated by "-". * The calendar type is used to lookup the {@code Chronology} using {@link #of(String)}. * - * @return the calendar system type, null if the calendar is not defined + * @return the calendar system type, null if the calendar is not defined by CLDR/LDML * @see #getId() */ public abstract String getCalendarType(); diff --git a/jdk/src/share/classes/java/time/chrono/Era.java b/jdk/src/share/classes/java/time/chrono/Era.java index a26953ee11a..330346dcc36 100644 --- a/jdk/src/share/classes/java/time/chrono/Era.java +++ b/jdk/src/share/classes/java/time/chrono/Era.java @@ -93,7 +93,7 @@ import java.util.Locale; *

      * Instances of {@code Era} may be compared using the {@code ==} operator. * - *

      Specification for implementors

      + * @implSpec * This interface must be implemented with care to ensure other classes operate correctly. * All implementations must be singletons - final, immutable and thread-safe. * It is recommended to use an enum whenever possible. diff --git a/jdk/src/share/classes/java/time/chrono/HijrahChronology.java b/jdk/src/share/classes/java/time/chrono/HijrahChronology.java index 5d53855d723..63dfc1c213b 100644 --- a/jdk/src/share/classes/java/time/chrono/HijrahChronology.java +++ b/jdk/src/share/classes/java/time/chrono/HijrahChronology.java @@ -133,9 +133,10 @@ import sun.util.logging.PlatformLogger; * Chronology chrono = Chronology.ofLocale(locale); * * - *

      Specification for implementors

      + * @implSpec * This class is immutable and thread-safe. - *

      Implementation Note for Hijrah Calendar Variant Configuration

      + * + * @implNote * Each Hijrah variant is configured individually. Each variant is defined by a * property resource that defines the {@code ID}, the {@code calendar type}, * the start of the calendar, the alignment with the @@ -229,6 +230,11 @@ public final class HijrahChronology extends Chronology implements Serializable { * {@link Chronology#getAvailableChronologies}. */ public static final HijrahChronology INSTANCE; + /** + * Flag to indicate the initialization of configuration data is complete. + * @see #checkCalendarInit() + */ + private volatile boolean initComplete; /** * Array of epoch days indexed by Hijrah Epoch month. * Computed by {@link #loadCalendarData}. @@ -285,7 +291,8 @@ public final class HijrahChronology extends Chronology implements Serializable { private static final String PROP_TYPE_SUFFIX = ".type"; /** - * Name data. + * Static initialization of the predefined calendars found in the + * lib/calendars.properties file. */ static { try { @@ -299,8 +306,7 @@ public final class HijrahChronology extends Chronology implements Serializable { // Register it by its aliases Chronology.registerChrono(INSTANCE, "Hijrah"); Chronology.registerChrono(INSTANCE, "islamic"); - - } catch (Exception ex) { + } catch (DateTimeException ex) { // Absence of Hijrah calendar is fatal to initializing this class. PlatformLogger logger = PlatformLogger.getLogger("java.time.chrono"); logger.severe("Unable to initialize Hijrah calendar: Hijrah-umalqura", ex); @@ -327,7 +333,7 @@ public final class HijrahChronology extends Chronology implements Serializable { // Create and register the variant HijrahChronology chrono = new HijrahChronology(id); Chronology.registerChrono(chrono); - } catch (Exception ex) { + } catch (DateTimeException ex) { // Log error and continue PlatformLogger logger = PlatformLogger.getLogger("java.time.chrono"); logger.severe("Unable to initialize Hijrah calendar: " + id, ex); @@ -343,22 +349,39 @@ public final class HijrahChronology extends Chronology implements Serializable { * The property names are {@code "calendar.hijrah." + id} * and {@code "calendar.hijrah." + id + ".type"} * @param id the id of the calendar - * @throws Exception if the resource can not be accessed or - * the format is invalid + * @throws DateTimeException if the calendar type is missing from the properties file. + * @throws IllegalArgumentException if the id is empty */ - private HijrahChronology(String id) throws Exception { + private HijrahChronology(String id) throws DateTimeException { if (id.isEmpty()) { throw new IllegalArgumentException("calendar id is empty"); } + String propName = PROP_PREFIX + id + PROP_TYPE_SUFFIX; + String calType = calendarProperties.getProperty(propName); + if (calType == null || calType.isEmpty()) { + throw new DateTimeException("calendarType is missing or empty for: " + propName); + } this.typeId = id; - this.calendarType = calendarProperties.getProperty(PROP_PREFIX + id + PROP_TYPE_SUFFIX); + this.calendarType = calType; + } - try { - String resource = calendarProperties.getProperty(PROP_PREFIX + id); - Objects.requireNonNull(resource, "Resource missing for calendar"); - loadCalendarData(resource); - } catch (Exception ex) { - throw new Exception("Unable to initialize HijrahCalendar: " + id, ex); + /** + * Check and ensure that the calendar data has been initialized. + * The initialization check is performed at the boundary between + * public and package methods. If a public calls another public method + * a check is not necessary in the caller. + * The constructors of HijrahDate call {@link #getEpochDay} or + * {@link #getHijrahDateInfo} so every call from HijrahDate to a + * HijrahChronology via package private methods has been checked. + * + * @throws DateTimeException if the calendar data configuration is + * malformed or IOExceptions occur loading the data + */ + private void checkCalendarInit() { + // Keep this short so it can be inlined for performance + if (initComplete == false) { + loadCalendarData(); + initComplete = true; } } @@ -509,6 +532,7 @@ public final class HijrahChronology extends Chronology implements Serializable { //----------------------------------------------------------------------- @Override public boolean isLeapYear(long prolepticYear) { + checkCalendarInit(); int epochMonth = yearToEpochMonth((int) prolepticYear); if (epochMonth < 0 || epochMonth > maxEpochDay) { throw new DateTimeException("Hijrah date out of range"); @@ -543,6 +567,7 @@ public final class HijrahChronology extends Chronology implements Serializable { //----------------------------------------------------------------------- @Override public ValueRange range(ChronoField field) { + checkCalendarInit(); if (field instanceof ChronoField) { ChronoField f = field; switch (f) { @@ -595,6 +620,7 @@ public final class HijrahChronology extends Chronology implements Serializable { * @return int[0] = YEAR, int[1] = MONTH, int[2] = DATE */ int[] getHijrahDateInfo(int epochDay) { + checkCalendarInit(); // ensure that the chronology is initialized if (epochDay < minEpochDay || epochDay >= maxEpochDay) { throw new DateTimeException("Hijrah date out of range"); } @@ -621,6 +647,7 @@ public final class HijrahChronology extends Chronology implements Serializable { * @return the epoch day */ long getEpochDay(int prolepticYear, int monthOfYear, int dayOfMonth) { + checkCalendarInit(); // ensure that the chronology is initialized checkValidMonth(monthOfYear); int epochMonth = yearToEpochMonth(prolepticYear) + (monthOfYear - 1); if (epochMonth < 0 || epochMonth >= hijrahEpochMonthStartDays.length) { @@ -846,84 +873,90 @@ public final class HijrahChronology extends Chronology implements Serializable { } /** - * Loads and processes the Hijrah calendar properties file. + * Loads and processes the Hijrah calendar properties file for this calendarType. * The starting Hijrah date and the corresponding ISO date are * extracted and used to calculate the epochDate offset. * The version number is identified and ignored. * Everything else is the data for a year with containing the length of each * of 12 months. * - * @param resourceName containing the properties defining the calendar, not null - * @throws IllegalArgumentException if any of the values are malformed - * @throws NumberFormatException if numbers, including properties that should - * be years are invalid - * @throws IOException if access to the property resource fails. + * @throws DateTimeException if initialization of the calendar data from the + * resource fails */ - private void loadCalendarData(String resourceName) throws Exception { - Properties props = readConfigProperties(resourceName); + private void loadCalendarData() { + try { + String resourceName = calendarProperties.getProperty(PROP_PREFIX + typeId); + Objects.requireNonNull(resourceName, "Resource missing for calendar: " + PROP_PREFIX + typeId); + Properties props = readConfigProperties(resourceName); - Map years = new HashMap<>(); - int minYear = Integer.MAX_VALUE; - int maxYear = Integer.MIN_VALUE; - String id = null; - String type = null; - String version = null; - int isoStart = 0; - for (Map.Entry entry : props.entrySet()) { - String key = (String) entry.getKey(); - switch (key) { - case KEY_ID: - id = (String)entry.getValue(); - break; - case KEY_TYPE: - type = (String)entry.getValue(); - break; - case KEY_VERSION: - version = (String)entry.getValue(); - break; - case KEY_ISO_START: { - int[] ymd = parseYMD((String) entry.getValue()); - isoStart = (int) LocalDate.of(ymd[0], ymd[1], ymd[2]).toEpochDay(); - break; - } - default: - try { - // Everything else is either a year or invalid - int year = Integer.valueOf(key); - int[] months = parseMonths((String) entry.getValue()); - years.put(year, months); - maxYear = Math.max(maxYear, year); - minYear = Math.min(minYear, year); - } catch (NumberFormatException nfe) { - throw new IllegalArgumentException("bad key: " + key); + Map years = new HashMap<>(); + int minYear = Integer.MAX_VALUE; + int maxYear = Integer.MIN_VALUE; + String id = null; + String type = null; + String version = null; + int isoStart = 0; + for (Map.Entry entry : props.entrySet()) { + String key = (String) entry.getKey(); + switch (key) { + case KEY_ID: + id = (String)entry.getValue(); + break; + case KEY_TYPE: + type = (String)entry.getValue(); + break; + case KEY_VERSION: + version = (String)entry.getValue(); + break; + case KEY_ISO_START: { + int[] ymd = parseYMD((String) entry.getValue()); + isoStart = (int) LocalDate.of(ymd[0], ymd[1], ymd[2]).toEpochDay(); + break; } + default: + try { + // Everything else is either a year or invalid + int year = Integer.valueOf(key); + int[] months = parseMonths((String) entry.getValue()); + years.put(year, months); + maxYear = Math.max(maxYear, year); + minYear = Math.min(minYear, year); + } catch (NumberFormatException nfe) { + throw new IllegalArgumentException("bad key: " + key); + } + } } - } - if (!getId().equals(id)) { - throw new IllegalArgumentException("Configuration is for a different calendar: " + id); - } - if (!getCalendarType().equals(type)) { - throw new IllegalArgumentException("Configuration is for a different calendar type: " + type); - } - if (version == null || version.isEmpty()) { - throw new IllegalArgumentException("Configuration does not contain a version"); - } - if (isoStart == 0) { - throw new IllegalArgumentException("Configuration does not contain a ISO start date"); - } + if (!getId().equals(id)) { + throw new IllegalArgumentException("Configuration is for a different calendar: " + id); + } + if (!getCalendarType().equals(type)) { + throw new IllegalArgumentException("Configuration is for a different calendar type: " + type); + } + if (version == null || version.isEmpty()) { + throw new IllegalArgumentException("Configuration does not contain a version"); + } + if (isoStart == 0) { + throw new IllegalArgumentException("Configuration does not contain a ISO start date"); + } - // Now create and validate the array of epochDays indexed by epochMonth - hijrahStartEpochMonth = minYear * 12; - minEpochDay = isoStart; - hijrahEpochMonthStartDays = createEpochMonths(minEpochDay, minYear, maxYear, years); - maxEpochDay = hijrahEpochMonthStartDays[hijrahEpochMonthStartDays.length - 1]; + // Now create and validate the array of epochDays indexed by epochMonth + hijrahStartEpochMonth = minYear * 12; + minEpochDay = isoStart; + hijrahEpochMonthStartDays = createEpochMonths(minEpochDay, minYear, maxYear, years); + maxEpochDay = hijrahEpochMonthStartDays[hijrahEpochMonthStartDays.length - 1]; - // Compute the min and max year length in days. - for (int year = minYear; year < maxYear; year++) { - int length = getYearLength(year); - minYearLength = Math.min(minYearLength, length); - maxYearLength = Math.max(maxYearLength, length); + // Compute the min and max year length in days. + for (int year = minYear; year < maxYear; year++) { + int length = getYearLength(year); + minYearLength = Math.min(minYearLength, length); + maxYearLength = Math.max(maxYearLength, length); + } + } catch (Exception ex) { + // Log error and throw a DateTimeException + PlatformLogger logger = PlatformLogger.getLogger("java.time.chrono"); + logger.severe("Unable to initialize Hijrah calendar proxy: " + typeId, ex); + throw new DateTimeException("Unable to initialize HijrahCalendar: " + typeId, ex); } } diff --git a/jdk/src/share/classes/java/time/chrono/HijrahDate.java b/jdk/src/share/classes/java/time/chrono/HijrahDate.java index fc9bd5b0e8e..3c01e94b980 100644 --- a/jdk/src/share/classes/java/time/chrono/HijrahDate.java +++ b/jdk/src/share/classes/java/time/chrono/HijrahDate.java @@ -102,7 +102,7 @@ import java.time.temporal.ValueRange; * to create new HijrahDate instances. * Alternatively, the {@link #withVariant} method can be used to convert * to a new HijrahChronology. - *

      Specification for implementors

      + * @implSpec * This class is immutable and thread-safe. * * @since 1.8 diff --git a/jdk/src/share/classes/java/time/chrono/HijrahEra.java b/jdk/src/share/classes/java/time/chrono/HijrahEra.java index 66622b567a4..1e99d6062ab 100644 --- a/jdk/src/share/classes/java/time/chrono/HijrahEra.java +++ b/jdk/src/share/classes/java/time/chrono/HijrahEra.java @@ -81,7 +81,7 @@ import java.time.temporal.ValueRange; * Do not use {@code ordinal()} to obtain the numeric representation of {@code HijrahEra}. * Use {@code getValue()} instead. * - *

      Specification for implementors

      + * @implSpec * This is an immutable and thread-safe enum. * * @since 1.8 diff --git a/jdk/src/share/classes/java/time/chrono/IsoChronology.java b/jdk/src/share/classes/java/time/chrono/IsoChronology.java index 3624e8d418e..66b0dc10d23 100644 --- a/jdk/src/share/classes/java/time/chrono/IsoChronology.java +++ b/jdk/src/share/classes/java/time/chrono/IsoChronology.java @@ -121,7 +121,7 @@ import java.util.Objects; *
    3. leap-year - Leap years occur every 4 years, except where the year is divisble by 100 and not divisble by 400. *

      * - *

      Specification for implementors

      + * @implSpec * This class is immutable and thread-safe. * * @since 1.8 @@ -588,7 +588,12 @@ public final class IsoChronology extends Chronology implements Serializable { int moy = MONTH_OF_YEAR.checkValidIntValue(fieldValues.remove(MONTH_OF_YEAR)); int dom = DAY_OF_MONTH.checkValidIntValue(fieldValues.remove(DAY_OF_MONTH)); if (resolverStyle == ResolverStyle.SMART) { // previous valid - dom = Math.min(dom, Month.of(moy).length(Year.isLeap(y))); + if (moy == 4 || moy == 6 || moy == 9 || moy == 11) { + dom = Math.min(dom, 30); + } else if (moy == 2) { + dom = Math.min(dom, Month.FEBRUARY.length(Year.isLeap(y))); + + } } return LocalDate.of(y, moy, dom); } diff --git a/jdk/src/share/classes/java/time/chrono/IsoEra.java b/jdk/src/share/classes/java/time/chrono/IsoEra.java index 445aa45478e..15fa2e4cc6d 100644 --- a/jdk/src/share/classes/java/time/chrono/IsoEra.java +++ b/jdk/src/share/classes/java/time/chrono/IsoEra.java @@ -97,7 +97,7 @@ import java.time.DateTimeException; * Do not use {@code ordinal()} to obtain the numeric representation of {@code IsoEra}. * Use {@code getValue()} instead. * - *

      Specification for implementors

      + * @implSpec * This is an immutable and thread-safe enum. * * @since 1.8 diff --git a/jdk/src/share/classes/java/time/chrono/JapaneseChronology.java b/jdk/src/share/classes/java/time/chrono/JapaneseChronology.java index 1d07a36929d..1aead71b8ad 100644 --- a/jdk/src/share/classes/java/time/chrono/JapaneseChronology.java +++ b/jdk/src/share/classes/java/time/chrono/JapaneseChronology.java @@ -85,7 +85,7 @@ import sun.util.calendar.LocalGregorianCalendar; * Only Meiji (1865-04-07 - 1868-09-07) and later eras are supported. * Older eras are handled as an unknown era where the year-of-era is the ISO year. * - *

      Specification for implementors

      + * @implSpec * This class is immutable and thread-safe. * * @since 1.8 @@ -197,7 +197,7 @@ public final class JapaneseChronology extends Chronology implements Serializable */ @Override public JapaneseDate dateYearDay(Era era, int yearOfEra, int dayOfYear) { - return dateYearDay(prolepticYear(era, yearOfEra), dayOfYear); + return JapaneseDate.ofYearDay((JapaneseEra) era, yearOfEra, dayOfYear); } /** @@ -251,16 +251,19 @@ public final class JapaneseChronology extends Chronology implements Serializable } @Override + @SuppressWarnings("unchecked") public ChronoLocalDateTime localDateTime(TemporalAccessor temporal) { return (ChronoLocalDateTime)super.localDateTime(temporal); } @Override + @SuppressWarnings("unchecked") public ChronoZonedDateTime zonedDateTime(TemporalAccessor temporal) { return (ChronoZonedDateTime)super.zonedDateTime(temporal); } @Override + @SuppressWarnings("unchecked") public ChronoZonedDateTime zonedDateTime(Instant instant, ZoneId zone) { return (ChronoZonedDateTime)super.zonedDateTime(instant, zone); } @@ -286,19 +289,27 @@ public final class JapaneseChronology extends Chronology implements Serializable if (era instanceof JapaneseEra == false) { throw new ClassCastException("Era must be JapaneseEra"); } + + if (era == JapaneseEra.SEIREKI) { + JapaneseEra nextEra = JapaneseEra.values()[1]; + int nextEraYear = nextEra.getPrivateEra().getSinceDate().getYear(); + if (yearOfEra >= nextEraYear || yearOfEra < Year.MIN_VALUE) { + throw new DateTimeException("Invalid yearOfEra value"); + } + return yearOfEra; + } + JapaneseEra jera = (JapaneseEra) era; int gregorianYear = jera.getPrivateEra().getSinceDate().getYear() + yearOfEra - 1; if (yearOfEra == 1) { return gregorianYear; } - LocalGregorianCalendar.Date jdate = JCAL.newCalendarDate(null); - jdate.setEra(jera.getPrivateEra()).setDate(yearOfEra, 1, 1); - if (!JapaneseChronology.JCAL.validate(jdate)) { - throw new DateTimeException("Invalid yearOfEra value"); - } - JCAL.normalize(jdate); - if (jdate.getNormalizedYear() == gregorianYear) { - return gregorianYear; + if (gregorianYear >= Year.MIN_VALUE && gregorianYear <= Year.MAX_VALUE) { + LocalGregorianCalendar.Date jdate = JCAL.newCalendarDate(null); + jdate.setEra(jera.getPrivateEra()).setDate(yearOfEra, 1, 1); + if (JapaneseChronology.JCAL.validate(jdate)) { + return gregorianYear; + } } throw new DateTimeException("Invalid yearOfEra value"); } @@ -322,13 +333,20 @@ public final class JapaneseChronology extends Chronology implements Serializable @Override public List eras() { - return Arrays.asList(JapaneseEra.values()); + return Arrays.asList(JapaneseEra.values()); + } + + JapaneseEra getCurrentEra() { + // Assume that the last JapaneseEra is the current one. + JapaneseEra[] eras = JapaneseEra.values(); + return eras[eras.length - 1]; } //----------------------------------------------------------------------- @Override public ValueRange range(ChronoField field) { switch (field) { + case YEAR: case DAY_OF_MONTH: case DAY_OF_WEEK: case MICRO_OF_DAY: @@ -345,27 +363,23 @@ public final class JapaneseChronology extends Chronology implements Serializable case NANO_OF_SECOND: case CLOCK_HOUR_OF_DAY: case CLOCK_HOUR_OF_AMPM: - case EPOCH_DAY: // TODO: if year is restricted, then so is epoch-day + case EPOCH_DAY: + case PROLEPTIC_MONTH: + case MONTH_OF_YEAR: return field.range(); + case ERA: + return ValueRange.of(JapaneseEra.SEIREKI.getValue(), + getCurrentEra().getValue()); } Calendar jcal = Calendar.getInstance(LOCALE); int fieldIndex; switch (field) { - case ERA: - return ValueRange.of(JapaneseEra.SEIREKI.getValue(), - jcal.getMaximum(Calendar.ERA) - JapaneseEra.ERA_OFFSET); - case YEAR: - case YEAR_OF_ERA: - // TODO: this is not right + case YEAR_OF_ERA: { + int startYear = getCurrentEra().getPrivateEra().getSinceDate().getYear(); return ValueRange.of(Year.MIN_VALUE, jcal.getGreatestMinimum(Calendar.YEAR), - jcal.getLeastMaximum(Calendar.YEAR), Year.MAX_VALUE); - case PROLEPTIC_MONTH: - // TODO: should be the range of months bound by the valid range of years - return ValueRange.of((jcal.getGreatestMinimum(Calendar.YEAR) - 1) * 12, - (jcal.getLeastMaximum(Calendar.YEAR)) * 12); - case MONTH_OF_YEAR: - return ValueRange.of(jcal.getMinimum(Calendar.MONTH) + 1, jcal.getGreatestMinimum(Calendar.MONTH) + 1, - jcal.getLeastMaximum(Calendar.MONTH) + 1, jcal.getMaximum(Calendar.MONTH) + 1); + jcal.getLeastMaximum(Calendar.YEAR) + 1, // +1 due to the different definitions + Year.MAX_VALUE - startYear); + } case DAY_OF_YEAR: fieldIndex = Calendar.DAY_OF_YEAR; break; diff --git a/jdk/src/share/classes/java/time/chrono/JapaneseDate.java b/jdk/src/share/classes/java/time/chrono/JapaneseDate.java index 7ba7fd37401..646315b3d80 100644 --- a/jdk/src/share/classes/java/time/chrono/JapaneseDate.java +++ b/jdk/src/share/classes/java/time/chrono/JapaneseDate.java @@ -83,6 +83,7 @@ import java.time.temporal.ValueRange; import java.util.Calendar; import java.util.Objects; +import sun.util.calendar.CalendarDate; import sun.util.calendar.LocalGregorianCalendar; /** @@ -101,7 +102,7 @@ import sun.util.calendar.LocalGregorianCalendar; * Calling {@code japaneseDate.get(ERA)} will return 2, corresponding to * {@code JapaneseChronology.ERA_HEISEI}.
      * - *

      Specification for implementors

      + * @implSpec * This class is immutable and thread-safe. * * @since 1.8 @@ -234,6 +235,24 @@ public final class JapaneseDate return of(prolepticYear, date.getMonthValue(), date.getDayOfMonth()); } + static JapaneseDate ofYearDay(JapaneseEra era, int yearOfEra, int dayOfYear) { + CalendarDate firstDay = era.getPrivateEra().getSinceDate(); + LocalGregorianCalendar.Date jdate = JapaneseChronology.JCAL.newCalendarDate(null); + jdate.setEra(era.getPrivateEra()); + if (yearOfEra == 1) { + jdate.setDate(yearOfEra, firstDay.getMonth(), firstDay.getDayOfMonth() + dayOfYear - 1); + } else { + jdate.setDate(yearOfEra, 1, dayOfYear); + } + JapaneseChronology.JCAL.normalize(jdate); + if (era.getPrivateEra() != jdate.getEra() || yearOfEra != jdate.getYear()) { + throw new DateTimeException("Invalid parameters"); + } + LocalDate localdate = LocalDate.of(jdate.getNormalizedYear(), + jdate.getMonth(), jdate.getDayOfMonth()); + return new JapaneseDate(era, yearOfEra, localdate); + } + /** * Obtains a {@code JapaneseDate} representing a date in the Japanese calendar * system from the era, year-of-era, month-of-year and day-of-month fields. diff --git a/jdk/src/share/classes/java/time/chrono/JapaneseEra.java b/jdk/src/share/classes/java/time/chrono/JapaneseEra.java index f89de19a271..17635793b08 100644 --- a/jdk/src/share/classes/java/time/chrono/JapaneseEra.java +++ b/jdk/src/share/classes/java/time/chrono/JapaneseEra.java @@ -91,7 +91,7 @@ import sun.util.calendar.CalendarDate; * and the year of era of Seireki is proleptic Gregorian year. * (The Julian to Gregorian transition is not supported.) * - *

      Specification for implementors

      + * @implSpec * This class is immutable and thread-safe. * * @since 1.8 diff --git a/jdk/src/share/classes/java/time/chrono/MinguoChronology.java b/jdk/src/share/classes/java/time/chrono/MinguoChronology.java index f977b496e0f..1c9c2e34c16 100644 --- a/jdk/src/share/classes/java/time/chrono/MinguoChronology.java +++ b/jdk/src/share/classes/java/time/chrono/MinguoChronology.java @@ -95,7 +95,7 @@ import java.util.Locale; * are never out of step. *

      * - *

      Specification for implementors

      + * @implSpec * This class is immutable and thread-safe. * * @since 1.8 diff --git a/jdk/src/share/classes/java/time/chrono/MinguoDate.java b/jdk/src/share/classes/java/time/chrono/MinguoDate.java index 07ce800ab06..b6a5582ef98 100644 --- a/jdk/src/share/classes/java/time/chrono/MinguoDate.java +++ b/jdk/src/share/classes/java/time/chrono/MinguoDate.java @@ -89,7 +89,7 @@ import java.util.Objects; * This calendar system is primarily used in the Republic of China, often known as Taiwan. * Dates are aligned such that {@code 0001-01-01 (Minguo)} is {@code 1912-01-01 (ISO)}. * - *

      Specification for implementors

      + * @implSpec * This class is immutable and thread-safe. * * @since 1.8 diff --git a/jdk/src/share/classes/java/time/chrono/MinguoEra.java b/jdk/src/share/classes/java/time/chrono/MinguoEra.java index 0563515834d..edf1ad5610a 100644 --- a/jdk/src/share/classes/java/time/chrono/MinguoEra.java +++ b/jdk/src/share/classes/java/time/chrono/MinguoEra.java @@ -102,7 +102,7 @@ import java.time.DateTimeException; * Do not use {@code ordinal()} to obtain the numeric representation of {@code MinguoEra}. * Use {@code getValue()} instead. * - *

      Specification for implementors

      + * @implSpec * This is an immutable and thread-safe enum. * * @since 1.8 diff --git a/jdk/src/share/classes/java/time/chrono/Ser.java b/jdk/src/share/classes/java/time/chrono/Ser.java index 5fec32da1a3..ff59aec4190 100644 --- a/jdk/src/share/classes/java/time/chrono/Ser.java +++ b/jdk/src/share/classes/java/time/chrono/Ser.java @@ -68,7 +68,7 @@ import java.time.LocalDateTime; /** * The shared serialization delegate for this package. * - *

      Implementation notes

      + * @implNote * This class wraps the object being serialized, and takes a byte representing the type of the class to * be serialized. This byte can also be used for versioning the serialization format. In this case another * byte flag would be used in order to specify an alternative version of the type format. diff --git a/jdk/src/share/classes/java/time/chrono/ThaiBuddhistChronology.java b/jdk/src/share/classes/java/time/chrono/ThaiBuddhistChronology.java index aee743526ab..27c98a6f079 100644 --- a/jdk/src/share/classes/java/time/chrono/ThaiBuddhistChronology.java +++ b/jdk/src/share/classes/java/time/chrono/ThaiBuddhistChronology.java @@ -96,7 +96,7 @@ import java.util.Locale; * are never out of step. *

      * - *

      Specification for implementors

      + * @implSpec * This class is immutable and thread-safe. * * @since 1.8 diff --git a/jdk/src/share/classes/java/time/chrono/ThaiBuddhistDate.java b/jdk/src/share/classes/java/time/chrono/ThaiBuddhistDate.java index b879a14e586..973c7b8ab0f 100644 --- a/jdk/src/share/classes/java/time/chrono/ThaiBuddhistDate.java +++ b/jdk/src/share/classes/java/time/chrono/ThaiBuddhistDate.java @@ -89,7 +89,7 @@ import java.util.Objects; * This calendar system is primarily used in Thailand. * Dates are aligned such that {@code 2484-01-01 (Buddhist)} is {@code 1941-01-01 (ISO)}. * - *

      Specification for implementors

      + * @implSpec * This class is immutable and thread-safe. * * @since 1.8 diff --git a/jdk/src/share/classes/java/time/chrono/ThaiBuddhistEra.java b/jdk/src/share/classes/java/time/chrono/ThaiBuddhistEra.java index d1fb55fc740..d91eb81d79d 100644 --- a/jdk/src/share/classes/java/time/chrono/ThaiBuddhistEra.java +++ b/jdk/src/share/classes/java/time/chrono/ThaiBuddhistEra.java @@ -102,7 +102,7 @@ import java.time.DateTimeException; * Do not use {@code ordinal()} to obtain the numeric representation of {@code ThaiBuddhistEra}. * Use {@code getValue()} instead. * - *

      Specification for implementors

      + * @implSpec * This is an immutable and thread-safe enum. * * @since 1.8 diff --git a/jdk/src/share/classes/java/time/format/DateTimeFormatter.java b/jdk/src/share/classes/java/time/format/DateTimeFormatter.java index 5c6bb2336b1..158c741dff4 100644 --- a/jdk/src/share/classes/java/time/format/DateTimeFormatter.java +++ b/jdk/src/share/classes/java/time/format/DateTimeFormatter.java @@ -77,6 +77,7 @@ import java.text.Format; import java.text.ParseException; import java.text.ParsePosition; import java.time.DateTimeException; +import java.time.Period; import java.time.ZoneId; import java.time.ZoneOffset; import java.time.chrono.Chronology; @@ -121,7 +122,7 @@ import java.util.Set; * *

      * In addition to the format, formatters can be created with desired Locale, - * Chronology, ZoneId, and formatting symbols. + * Chronology, ZoneId, and DecimalStyle. *

      * The {@link #withLocale withLocale} method returns a new formatter that * overrides the locale. The locale affects some aspects of formatting and @@ -138,8 +139,8 @@ import java.util.Set; * with the requested ZoneId before formatting. During parsing the ZoneId is * applied before the value is returned. *

      - * The {@link #withSymbols withSymbols} method returns a new formatter that - * overrides the {@link DateTimeFormatSymbols}. The symbols are used for + * The {@link #withDecimalStyle withDecimalStyle} method returns a new formatter that + * overrides the {@link DecimalStyle}. The DecimalStyle symbols are used for * formatting and parsing. *

      * Some applications may need to use the older {@link Format java.text.Format} @@ -417,7 +418,65 @@ import java.util.Set; * that you want to output directly to ensure that future changes do not break * your application. * - *

      Specification for implementors

      + *

      Resolving

      + * Parsing is implemented as a two-phase operation. + * First, the text is parsed using the layout defined by the formatter, producing + * a {@code Map} of field to value, a {@code ZoneId} and a {@code Chronology}. + * Second, the parsed data is resolved, by validating, combining and + * simplifying the various fields into more useful ones. + *

      + * Five parsing methods are supplied by this class. + * Four of these perform both the parse and resolve phases. + * The fifth method, {@link #parseUnresolved(CharSequence, ParsePosition)}, + * only performs the first phase, leaving the result unresolved. + * As such, it is essentially a low-level operation. + *

      + * The resolve phase is controlled by two parameters, set on this class. + *

      + * The {@link ResolverStyle} is an enum that offers three different approaches, + * strict, smart and lenient. The smart option is the default. + * It can be set using {@link #withResolverStyle(ResolverStyle)}. + *

      + * The {@link #withResolverFields(TemporalField...)} parameter allows the + * set of fields that will be resolved to be filtered before resolving starts. + * For example, if the formatter has parsed a year, month, day-of-month + * and day-of-year, then there are two approaches to resolve a date: + * (year + month + day-of-month) and (year + day-of-year). + * The resolver fields allows one of the two approaches to be selected. + * If no resolver fields are set then both approaches must result in the same date. + *

      + * Resolving separate fields to form a complete date and time is a complex + * process with behaviour distributed across a number of classes. + * It follows these steps: + *

        + *
      1. The chronology is determined. + * The chronology of the result is either the chronology that was parsed, + * or if no chronology was parsed, it is the chronology set on this class, + * or if that is null, it is {@code IsoChronology}. + *
      2. The {@code ChronoField} date fields are resolved. + * This is achieved using {@link Chronology#resolveDate(Map, ResolverStyle)}. + * Documentation about field resolution is located in the implementation + * of {@code Chronology}. + *
      3. The {@code ChronoField} time fields are resolved. + * This is documented on {@link ChronoField} and is the same for all chronologies. + *
      4. Any fields that are not {@code ChronoField} are processed. + * This is achieved using {@link TemporalField#resolve(TemporalAccessor, long, ResolverStyle)}. + * Documentation about field resolution is located in the implementation + * of {@code TemporalField}. + *
      5. The {@code ChronoField} date and time fields are re-resolved. + * This allows fields in step four to produce {@code ChronoField} values + * and have them be processed into dates and times. + *
      6. A {@code LocalTime} is formed if there is at least an hour-of-day available. + * This involves providing default values for minute, second and fraction of second. + *
      7. Any remaining unresolved fields are cross-checked against any + * date and/or time that was resolved. Thus, an earlier stage would resolve + * (year + month + day-of-month) to a date, and this stage would check that + * day-of-week was valid for the date. + *
      8. If an {@linkplain #parsedExcessDays() excess number of days} + * was parsed then it is added to the date if a date is available. + *
      + * + * @implSpec * This class is immutable and thread-safe. * * @since 1.8 @@ -435,7 +494,7 @@ public final class DateTimeFormatter { /** * The symbols to use for formatting, not null. */ - private final DateTimeFormatSymbols symbols; + private final DecimalStyle decimalStyle; /** * The resolver style to use, not null. */ @@ -1040,6 +1099,11 @@ public final class DateTimeFormatter { *

      * This returns an immutable formatter capable of formatting and parsing * the ISO-8601 instant format. + * When formatting, the second-of-minute is always output. + * The nano-of-second outputs zero, three, six or nine digits digits as necessary. + * When parsing, time to at least the seconds field is required. + * Fractional seconds from zero to nine are parsed. + * The localized decimal style is not used. *

      * This is a special case formatter intended to allow a human readable form * of an {@link java.time.Instant}. The {@code Instant} class is designed to @@ -1201,25 +1265,117 @@ public final class DateTimeFormatter { .toFormatter(ResolverStyle.SMART, IsoChronology.INSTANCE); } + //----------------------------------------------------------------------- + /** + * A query that provides access to the excess days that were parsed. + *

      + * This returns a singleton {@linkplain TemporalQuery query} that provides + * access to additional information from the parse. The query always returns + * a non-null period, with a zero period returned instead of null. + *

      + * There are two situations where this query may return a non-zero period. + *

        + *
      • If the {@code ResolverStyle} is {@code LENIENT} and a time is parsed + * without a date, then the complete result of the parse consists of a + * {@code LocalTime} and an excess {@code Period} in days. + *

        + *

      • If the {@code ResolverStyle} is {@code SMART} and a time is parsed + * without a date where the time is 24:00:00, then the complete result of + * the parse consists of a {@code LocalTime} of 00:00:00 and an excess + * {@code Period} of one day. + *
      + *

      + * In both cases, if a complete {@code ChronoLocalDateTime} or {@code Instant} + * is parsed, then the excess days are added to the date part. + * As a result, this query will return a zero period. + *

      + * The {@code SMART} behaviour handles the common "end of day" 24:00 value. + * Processing in {@code LENIENT} mode also produces the same result: + *

      +     *  Text to parse        Parsed object                         Excess days
      +     *  "2012-12-03T00:00"   LocalDateTime.of(2012, 12, 3, 0, 0)   ZERO
      +     *  "2012-12-03T24:00"   LocalDateTime.of(2012, 12, 4, 0, 0)   ZERO
      +     *  "00:00"              LocalTime.of(0, 0)                    ZERO
      +     *  "24:00"              LocalTime.of(0, 0)                    Period.ofDays(1)
      +     * 
      + * The query can be used as follows: + *
      +     *  TemporalAccessor parsed = formatter.parse(str);
      +     *  LocalTime time = parsed.query(LocalTime::from);
      +     *  Period extraDays = parsed.query(DateTimeFormatter.parsedExcessDays());
      +     * 
      + */ + public static final TemporalQuery parsedExcessDays() { + return PARSED_EXCESS_DAYS; + } + private static final TemporalQuery PARSED_EXCESS_DAYS = t -> { + if (t instanceof Parsed) { + return ((Parsed) t).excessDays; + } else { + return Period.ZERO; + } + }; + + /** + * A query that provides access to whether a leap-second was parsed. + *

      + * This returns a singleton {@linkplain TemporalQuery query} that provides + * access to additional information from the parse. The query always returns + * a non-null boolean, true if parsing saw a leap-second, false if not. + *

      + * Instant parsing handles the special "leap second" time of '23:59:60'. + * Leap seconds occur at '23:59:60' in the UTC time-zone, but at other + * local times in different time-zones. To avoid this potential ambiguity, + * the handling of leap-seconds is limited to + * {@link DateTimeFormatterBuilder#appendInstant()}, as that method + * always parses the instant with the UTC zone offset. + *

      + * If the time '23:59:60' is received, then a simple conversion is applied, + * replacing the second-of-minute of 60 with 59. This query can be used + * on the parse result to determine if the leap-second adjustment was made. + * The query will return one second of excess if it did adjust to remove + * the leap-second, and zero if not. Note that applying a leap-second + * smoothing mechanism, such as UTC-SLS, is the responsibility of the + * application, as follows: + *

      +     *  TemporalAccessor parsed = formatter.parse(str);
      +     *  Instant instant = parsed.query(Instant::from);
      +     *  if (parsed.query(DateTimeFormatter.parsedLeapSecond())) {
      +     *    // validate leap-second is correct and apply correct smoothing
      +     *  }
      +     * 
      + */ + public static final TemporalQuery parsedLeapSecond() { + return PARSED_LEAP_SECOND; + } + private static final TemporalQuery PARSED_LEAP_SECOND = t -> { + if (t instanceof Parsed) { + return ((Parsed) t).leapSecond; + } else { + return Boolean.FALSE; + } + }; + + //----------------------------------------------------------------------- /** * Constructor. * * @param printerParser the printer/parser to use, not null * @param locale the locale to use, not null - * @param symbols the symbols to use, not null + * @param decimalStyle the DecimalStyle to use, not null * @param resolverStyle the resolver style to use, not null * @param resolverFields the fields to use during resolving, null for all fields * @param chrono the chronology to use, null for no override * @param zone the zone to use, null for no override */ DateTimeFormatter(CompositePrinterParser printerParser, - Locale locale, DateTimeFormatSymbols symbols, + Locale locale, DecimalStyle decimalStyle, ResolverStyle resolverStyle, Set resolverFields, Chronology chrono, ZoneId zone) { this.printerParser = Objects.requireNonNull(printerParser, "printerParser"); this.resolverFields = resolverFields; this.locale = Objects.requireNonNull(locale, "locale"); - this.symbols = Objects.requireNonNull(symbols, "symbols"); + this.decimalStyle = Objects.requireNonNull(decimalStyle, "decimalStyle"); this.resolverStyle = Objects.requireNonNull(resolverStyle, "resolverStyle"); this.chrono = chrono; this.zone = zone; @@ -1253,32 +1409,32 @@ public final class DateTimeFormatter { if (this.locale.equals(locale)) { return this; } - return new DateTimeFormatter(printerParser, locale, symbols, resolverStyle, resolverFields, chrono, zone); + return new DateTimeFormatter(printerParser, locale, decimalStyle, resolverStyle, resolverFields, chrono, zone); } //----------------------------------------------------------------------- /** - * Gets the set of symbols to be used during formatting. + * Gets the DecimalStyle to be used during formatting. * * @return the locale of this formatter, not null */ - public DateTimeFormatSymbols getSymbols() { - return symbols; + public DecimalStyle getDecimalStyle() { + return decimalStyle; } /** - * Returns a copy of this formatter with a new set of symbols. + * Returns a copy of this formatter with a new DecimalStyle. *

      * This instance is immutable and unaffected by this method call. * - * @param symbols the new symbols, not null - * @return a formatter based on this formatter with the requested symbols, not null + * @param decimalStyle the new DecimalStyle, not null + * @return a formatter based on this formatter with the requested DecimalStyle, not null */ - public DateTimeFormatter withSymbols(DateTimeFormatSymbols symbols) { - if (this.symbols.equals(symbols)) { + public DateTimeFormatter withDecimalStyle(DecimalStyle decimalStyle) { + if (this.decimalStyle.equals(decimalStyle)) { return this; } - return new DateTimeFormatter(printerParser, locale, symbols, resolverStyle, resolverFields, chrono, zone); + return new DateTimeFormatter(printerParser, locale, decimalStyle, resolverStyle, resolverFields, chrono, zone); } //----------------------------------------------------------------------- @@ -1332,7 +1488,7 @@ public final class DateTimeFormatter { if (Objects.equals(this.chrono, chrono)) { return this; } - return new DateTimeFormatter(printerParser, locale, symbols, resolverStyle, resolverFields, chrono, zone); + return new DateTimeFormatter(printerParser, locale, decimalStyle, resolverStyle, resolverFields, chrono, zone); } //----------------------------------------------------------------------- @@ -1389,7 +1545,7 @@ public final class DateTimeFormatter { if (Objects.equals(this.zone, zone)) { return this; } - return new DateTimeFormatter(printerParser, locale, symbols, resolverStyle, resolverFields, chrono, zone); + return new DateTimeFormatter(printerParser, locale, decimalStyle, resolverStyle, resolverFields, chrono, zone); } //----------------------------------------------------------------------- @@ -1431,7 +1587,7 @@ public final class DateTimeFormatter { if (Objects.equals(this.resolverStyle, resolverStyle)) { return this; } - return new DateTimeFormatter(printerParser, locale, symbols, resolverStyle, resolverFields, chrono, zone); + return new DateTimeFormatter(printerParser, locale, decimalStyle, resolverStyle, resolverFields, chrono, zone); } //----------------------------------------------------------------------- @@ -1495,7 +1651,7 @@ public final class DateTimeFormatter { return this; } fields = Collections.unmodifiableSet(fields); - return new DateTimeFormatter(printerParser, locale, symbols, resolverStyle, fields, chrono, zone); + return new DateTimeFormatter(printerParser, locale, decimalStyle, resolverStyle, fields, chrono, zone); } /** @@ -1543,7 +1699,7 @@ public final class DateTimeFormatter { return this; } resolverFields = Collections.unmodifiableSet(new HashSet<>(resolverFields)); - return new DateTimeFormatter(printerParser, locale, symbols, resolverStyle, resolverFields, chrono, zone); + return new DateTimeFormatter(printerParser, locale, decimalStyle, resolverStyle, resolverFields, chrono, zone); } //----------------------------------------------------------------------- diff --git a/jdk/src/share/classes/java/time/format/DateTimeFormatterBuilder.java b/jdk/src/share/classes/java/time/format/DateTimeFormatterBuilder.java index d209f5c8baf..cec07840439 100644 --- a/jdk/src/share/classes/java/time/format/DateTimeFormatterBuilder.java +++ b/jdk/src/share/classes/java/time/format/DateTimeFormatterBuilder.java @@ -77,6 +77,7 @@ import java.math.BigInteger; import java.math.RoundingMode; import java.text.ParsePosition; import java.time.DateTimeException; +import java.time.Duration; import java.time.Instant; import java.time.LocalDateTime; import java.time.ZoneId; @@ -142,7 +143,7 @@ import sun.util.locale.provider.TimeZoneNameUtility; * can be used, see {@link #appendPattern(String)}. * In practice, this simply parses the pattern and calls other methods on the builder. * - *

      Specification for implementors

      + * @implSpec * This class is a mutable builder intended for use from a single thread. * * @since 1.8 @@ -186,6 +187,44 @@ public final class DateTimeFormatterBuilder { */ private int valueParserIndex = -1; + /** + * Gets the formatting pattern for date and time styles for a locale and chronology. + * The locale and chronology are used to lookup the locale specific format + * for the requested dateStyle and/or timeStyle. + * + * @param dateStyle the FormatStyle for the date + * @param timeStyle the FormatStyle for the time + * @param chrono the Chronology, non-null + * @param locale the locale, non-null + * @return the locale and Chronology specific formatting pattern + * @throws IllegalArgumentException if both dateStyle and timeStyle are null + */ + public static String getLocalizedDateTimePattern(FormatStyle dateStyle, FormatStyle timeStyle, + Chronology chrono, Locale locale) { + Objects.requireNonNull(locale, "locale"); + Objects.requireNonNull(chrono, "chrono"); + if (dateStyle == null && timeStyle == null) { + throw new IllegalArgumentException("Either dateStyle or timeStyle must be non-null"); + } + LocaleResources lr = LocaleProviderAdapter.getResourceBundleBased().getLocaleResources(locale); + String pattern = lr.getJavaTimeDateTimePattern( + convertStyle(timeStyle), convertStyle(dateStyle), chrono.getCalendarType()); + return pattern; + } + + /** + * Converts the given FormatStyle to the java.text.DateFormat style. + * + * @param style the FormatStyle style + * @return the int style, or -1 if style is null, indicating un-required + */ + private static int convertStyle(FormatStyle style) { + if (style == null) { + return -1; + } + return style.ordinal(); // indices happen to align + } + /** * Constructs a new instance of the builder. */ @@ -344,7 +383,7 @@ public final class DateTimeFormatterBuilder { */ public DateTimeFormatterBuilder appendValue(TemporalField field) { Objects.requireNonNull(field, "field"); - active.valueParserIndex = appendInternal(new NumberPrinterParser(field, 1, 19, SignStyle.NORMAL)); + appendValue(new NumberPrinterParser(field, 1, 19, SignStyle.NORMAL)); return this; } @@ -360,15 +399,15 @@ public final class DateTimeFormatterBuilder { * If the value of the field is negative then an exception is thrown during formatting. *

      * This method supports a special technique of parsing known as 'adjacent value parsing'. - * This technique solves the problem where a variable length value is followed by one or more + * This technique solves the problem where a value, variable or fixed width, is followed by one or more * fixed length values. The standard parser is greedy, and thus it would normally * steal the digits that are needed by the fixed width value parsers that follow the * variable width one. *

      * No action is required to initiate 'adjacent value parsing'. - * When a call to {@code appendValue} with a variable width is made, the builder + * When a call to {@code appendValue} is made, the builder * enters adjacent value parsing setup mode. If the immediately subsequent method - * call or calls on the same builder are to this method, then the parser will reserve + * call or calls on the same builder are for a fixed width value, then the parser will reserve * space so that the fixed width values can be parsed. *

      * For example, consider {@code builder.appendValue(YEAR).appendValue(MONTH_OF_YEAR, 2);} @@ -381,7 +420,7 @@ public final class DateTimeFormatterBuilder { * nothing for the month. *

      * Adjacent value parsing applies to each set of fixed width not-negative values in the parser - * that immediately follow any kind of variable width value. + * that immediately follow any kind of value, variable or fixed width. * Calling any other append method will end the setup of adjacent value parsing. * Thus, in the unlikely event that you need to avoid adjacent value parsing behavior, * simply add the {@code appendValue} to another {@code DateTimeFormatterBuilder} @@ -402,7 +441,8 @@ public final class DateTimeFormatterBuilder { throw new IllegalArgumentException("The width must be from 1 to 19 inclusive but was " + width); } NumberPrinterParser pp = new NumberPrinterParser(field, width, width, SignStyle.NOT_NEGATIVE); - return appendFixedWidth(width, pp); + appendValue(pp); + return this; } /** @@ -420,8 +460,10 @@ public final class DateTimeFormatterBuilder { * This behavior can be affected by 'adjacent value parsing'. * See {@link #appendValue(java.time.temporal.TemporalField, int)} for full details. *

      - * In strict parsing mode, the minimum number of parsed digits is {@code minWidth}. - * In lenient parsing mode, the minimum number of parsed digits is one. + * In strict parsing mode, the minimum number of parsed digits is {@code minWidth} + * and the maximum is {@code maxWidth}. + * In lenient parsing mode, the minimum number of parsed digits is one + * and the maximum is 19 (except as limited by adjacent value parsing). *

      * If this method is invoked with equal minimum and maximum widths and a sign style of * {@code NOT_NEGATIVE} then it delegates to {@code appendValue(TemporalField,int)}. @@ -452,17 +494,13 @@ public final class DateTimeFormatterBuilder { maxWidth + " < " + minWidth); } NumberPrinterParser pp = new NumberPrinterParser(field, minWidth, maxWidth, signStyle); - if (minWidth == maxWidth) { - appendInternal(pp); - } else { - active.valueParserIndex = appendInternal(pp); - } + appendValue(pp); return this; } //----------------------------------------------------------------------- /** - * Appends the reduced value of a date-time field to the formatter. + * Appends the reduced value of a date-time field with fixed width to the formatter. *

      * This is typically used for formatting and parsing a two digit year. * The {@code width} is the printed and parsed width. @@ -471,51 +509,116 @@ public final class DateTimeFormatterBuilder { * For formatting, the width is used to determine the number of characters to format. * The rightmost characters are output to match the width, left padding with zero. *

      - * For parsing, exactly the number of characters specified by the width are parsed. - * This is incomplete information however, so the base value is used to complete the parse. - * The base value is the first valid value in a range of ten to the power of width. + * For strict parsing, the number of characters allowed by the width are parsed. + * For lenient parsing, the number of characters must be at least 1 and less than 10. + * If the number of digits parsed is equal to {@code width} and the value is positive, + * the value of the field is computed to be the first number greater than + * or equal to the {@code baseValue} with the same least significant characters, + * otherwise the value parsed is the field value. + * This allows a reduced value to be entered for values in range of the baseValue + * and width and absolute values can be entered for values outside the range. *

      * For example, a base value of {@code 1980} and a width of {@code 2} will have * valid values from {@code 1980} to {@code 2079}. * During parsing, the text {@code "12"} will result in the value {@code 2012} as that - * is the value within the range where the last two digits are "12". - *

      - * This is a fixed width parser operating using 'adjacent value parsing'. - * See {@link #appendValue(java.time.temporal.TemporalField, int)} for full details. + * is the value within the range where the last two characters are "12". + * Compare with lenient parsing the text {@code "1915"} that will result in the + * value {@code 1915}. * * @param field the field to append, not null - * @param width the width of the printed and parsed field, from 1 to 18 + * @param width the field width of the printed and parsed field, from 1 to 10 + * @param baseValue the base value of the range of valid values + * @return this, for chaining, not null + * @throws IllegalArgumentException if the width or base value is invalid + * @see #appendValueReduced(java.time.temporal.TemporalField, int, int, int) + */ + public DateTimeFormatterBuilder appendValueReduced(TemporalField field, + int width, int baseValue) { + return appendValueReduced(field, width, width, baseValue); + } + + /** + * Appends the reduced value of a date-time field with a flexible width to the formatter. + *

      + * This is typically used for formatting and parsing a two digit year + * but allowing for the year value to be up to maxWidth. + *

      + * For formatting, the {@code width} and {@code maxWidth} are used to + * determine the number of characters to format. + * If the value of the field is within the range of the {@code baseValue} using + * {@code width} characters then the reduced value is formatted otherwise the value is + * truncated to fit {@code maxWidth}. + * The rightmost characters are output to match the width, left padding with zero. + *

      + * For strict parsing, the number of characters allowed by {@code width} to {@code maxWidth} are parsed. + * For lenient parsing, the number of characters must be at least 1 and less than 10. + * If the number of digits parsed is equal to {@code width} and the value is positive, + * the value of the field is computed to be the first number greater than + * or equal to the {@code baseValue} with the same least significant characters, + * otherwise the value parsed is the field value. + * This allows a reduced value to be entered for values in range of the baseValue + * and width and absolute values can be entered for values outside the range. + *

      + * For example, a base value of {@code 1980} and a width of {@code 2} will have + * valid values from {@code 1980} to {@code 2079}. + * During parsing, the text {@code "12"} will result in the value {@code 2012} as that + * is the value within the range where the last two characters are "12". + * Compare with parsing the text {@code "1915"} that will result in the + * value {@code 1915}. + * + * @param field the field to append, not null + * @param width the field width of the printed and parsed field, from 1 to 10 + * @param maxWidth the maximum field width of the printed field, from 1 to 10 * @param baseValue the base value of the range of valid values * @return this, for chaining, not null * @throws IllegalArgumentException if the width or base value is invalid */ - public DateTimeFormatterBuilder appendValueReduced( - TemporalField field, int width, int baseValue) { + public DateTimeFormatterBuilder appendValueReduced(TemporalField field, + int width, int maxWidth, int baseValue) { Objects.requireNonNull(field, "field"); - ReducedPrinterParser pp = new ReducedPrinterParser(field, width, baseValue); - appendFixedWidth(width, pp); + ReducedPrinterParser pp = new ReducedPrinterParser(field, width, maxWidth, baseValue); + appendValue(pp); return this; } /** - * Appends a fixed width printer-parser. + * Appends a fixed or variable width printer-parser handling adjacent value mode. + * If a PrinterParser is not active then the new PrinterParser becomes + * the active PrinterParser. + * Otherwise, the active PrinterParser is modified depending on the new PrinterParser. + * If the new PrinterParser is fixed width and has sign style {@code NOT_NEGATIVE} + * then its width is added to the active PP and + * the new PrinterParser is forced to be fixed width. + * If the new PrinterParser is variable width, the active PrinterParser is changed + * to be fixed width and the new PrinterParser becomes the active PP. * - * @param width the width * @param pp the printer-parser, not null * @return this, for chaining, not null */ - private DateTimeFormatterBuilder appendFixedWidth(int width, NumberPrinterParser pp) { + private DateTimeFormatterBuilder appendValue(NumberPrinterParser pp) { if (active.valueParserIndex >= 0) { + final int activeValueParser = active.valueParserIndex; + // adjacent parsing mode, update setting in previous parsers - NumberPrinterParser basePP = (NumberPrinterParser) active.printerParsers.get(active.valueParserIndex); - basePP = basePP.withSubsequentWidth(width); - int activeValueParser = active.valueParserIndex; - active.printerParsers.set(active.valueParserIndex, basePP); - appendInternal(pp.withFixedWidth()); - active.valueParserIndex = activeValueParser; + NumberPrinterParser basePP = (NumberPrinterParser) active.printerParsers.get(activeValueParser); + if (pp.minWidth == pp.maxWidth && pp.signStyle == SignStyle.NOT_NEGATIVE) { + // Append the width to the subsequentWidth of the active parser + basePP = basePP.withSubsequentWidth(pp.maxWidth); + // Append the new parser as a fixed width + appendInternal(pp.withFixedWidth()); + // Retain the previous active parser + active.valueParserIndex = activeValueParser; + } else { + // Modify the active parser to be fixed width + basePP = basePP.withFixedWidth(); + // The new parser becomes the mew active parser + active.valueParserIndex = appendInternal(pp); + } + // Replace the modified parser with the updated one + active.printerParsers.set(activeValueParser, basePP); } else { - // not adjacent parsing - appendInternal(pp); + // The new Parser becomes the active parser + active.valueParserIndex = appendInternal(pp); } return this; } @@ -657,11 +760,24 @@ public final class DateTimeFormatterBuilder { //----------------------------------------------------------------------- /** - * Appends an instant using ISO-8601 to the formatter. + * Appends an instant using ISO-8601 to the formatter, formatting fractional + * digits in groups of three. *

      * Instants have a fixed output format. - * They are converted to a date-time with a zone-offset of UTC and printed + * They are converted to a date-time with a zone-offset of UTC and formatted * using the standard ISO-8601 format. + * With this method, formatting nano-of-second outputs zero, three, six + * or nine digits digits as necessary. + * The localized decimal style is not used. + *

      + * The instant is obtained using {@link ChronoField#INSTANT_SECONDS INSTANT_SECONDS} + * and optionally (@code NANO_OF_SECOND). The value of {@code INSTANT_SECONDS} + * may be outside the maximum range of {@code LocalDateTime}. + *

      + * The {@linkplain ResolverStyle resolver style} has no effect on instant parsing. + * The end-of-day time of '24:00' is handled as midnight at the start of the following day. + * The leap-second time of '23:59:59' is handled to some degree, see + * {@link DateTimeFormatter#parsedLeapSecond()} for full details. *

      * An alternative to this method is to format/parse the instant as a single * epoch-seconds value. That is achieved using {@code appendValue(INSTANT_SECONDS)}. @@ -669,10 +785,54 @@ public final class DateTimeFormatterBuilder { * @return this, for chaining, not null */ public DateTimeFormatterBuilder appendInstant() { - appendInternal(new InstantPrinterParser()); + appendInternal(new InstantPrinterParser(-2)); return this; } + /** + * Appends an instant using ISO-8601 to the formatter with control over + * the number of fractional digits. + *

      + * Instants have a fixed output format, although this method provides some + * control over the fractional digits. They are converted to a date-time + * with a zone-offset of UTC and printed using the standard ISO-8601 format. + * The localized decimal style is not used. + *

      + * The {@code fractionalDigits} parameter allows the output of the fractional + * second to be controlled. Specifying zero will cause no fractional digits + * to be output. From 1 to 9 will output an increasing number of digits, using + * zero right-padding if necessary. The special value -1 is used to output as + * many digits as necessary to avoid any trailing zeroes. + *

      + * When parsing in strict mode, the number of parsed digits must match the + * fractional digits. When parsing in lenient mode, any number of fractional + * digits from zero to nine are accepted. + *

      + * The instant is obtained using {@link ChronoField#INSTANT_SECONDS INSTANT_SECONDS} + * and optionally (@code NANO_OF_SECOND). The value of {@code INSTANT_SECONDS} + * may be outside the maximum range of {@code LocalDateTime}. + *

      + * The {@linkplain ResolverStyle resolver style} has no effect on instant parsing. + * The end-of-day time of '24:00' is handled as midnight at the start of the following day. + * The leap-second time of '23:59:59' is handled to some degree, see + * {@link DateTimeFormatter#parsedLeapSecond()} for full details. + *

      + * An alternative to this method is to format/parse the instant as a single + * epoch-seconds value. That is achieved using {@code appendValue(INSTANT_SECONDS)}. + * + * @param fractionalDigits the number of fractional second digits to format with, + * from 0 to 9, or -1 to use as many digits as necessary + * @return this, for chaining, not null + */ + public DateTimeFormatterBuilder appendInstant(int fractionalDigits) { + if (fractionalDigits < -1 || fractionalDigits > 9) { + throw new IllegalArgumentException("The fractional digits must be from -1 to 9 inclusive but was " + fractionalDigits); + } + appendInternal(new InstantPrinterParser(fractionalDigits)); + return this; + } + + //----------------------------------------------------------------------- /** * Appends the zone offset, such as '+01:00', to the formatter. *

      @@ -1049,7 +1209,7 @@ public final class DateTimeFormatterBuilder { *

      * The calendar system name will be output during a format. * If the chronology cannot be obtained then an exception will be thrown. - * The calendar system name is obtained from the formatting symbols. + * The calendar system name is obtained from the Chronology. * * @param textStyle the text style to use, not null * @return this, for chaining, not null @@ -1838,7 +1998,7 @@ public final class DateTimeFormatterBuilder { * using the default locale. *

      * This will create a formatter with the {@linkplain Locale#getDefault(Locale.Category) default FORMAT locale}. - * Numbers will be printed and parsed using the standard non-localized set of symbols. + * Numbers will be printed and parsed using the standard DecimalStyle. * The resolver style will be {@link ResolverStyle#SMART SMART}. *

      * Calling this method will end any open optional sections by repeatedly @@ -1858,7 +2018,7 @@ public final class DateTimeFormatterBuilder { * using the specified locale. *

      * This will create a formatter with the specified locale. - * Numbers will be printed and parsed using the standard non-localized set of symbols. + * Numbers will be printed and parsed using the standard DecimalStyle. * The resolver style will be {@link ResolverStyle#SMART SMART}. *

      * Calling this method will end any open optional sections by repeatedly @@ -1898,7 +2058,7 @@ public final class DateTimeFormatterBuilder { optionalEnd(); } CompositePrinterParser pp = new CompositePrinterParser(printerParsers, false); - return new DateTimeFormatter(pp, locale, DateTimeFormatSymbols.STANDARD, + return new DateTimeFormatter(pp, locale, DecimalStyle.STANDARD, resolverStyle, null, chrono, null); } @@ -1921,7 +2081,7 @@ public final class DateTimeFormatterBuilder { * for the next parser. If an error occurs, the returned index will be negative * and will have the error position encoded using the complement operator. * - *

      Specification for implementors

      + * @implSpec * This interface must be implemented with care to ensure other classes operate correctly. * All implementations that can be instantiated must be final, immutable and thread-safe. *

      @@ -2282,24 +2442,25 @@ public final class DateTimeFormatterBuilder { /** * Array of 10 to the power of n. */ - static final int[] EXCEED_POINTS = new int[] { - 0, - 10, - 100, - 1000, - 10000, - 100000, - 1000000, - 10000000, - 100000000, - 1000000000, + static final long[] EXCEED_POINTS = new long[] { + 0L, + 10L, + 100L, + 1000L, + 10000L, + 100000L, + 1000000L, + 10000000L, + 100000000L, + 1000000000L, + 10000000000L, }; final TemporalField field; final int minWidth; - private final int maxWidth; + final int maxWidth; private final SignStyle signStyle; - private final int subsequentWidth; + final int subsequentWidth; /** * Constructor. @@ -2328,7 +2489,7 @@ public final class DateTimeFormatterBuilder { * @param subsequentWidth the width of subsequent non-negative numbers, 0 or greater, * -1 if fixed width due to active adjacent parsing */ - private NumberPrinterParser(TemporalField field, int minWidth, int maxWidth, SignStyle signStyle, int subsequentWidth) { + protected NumberPrinterParser(TemporalField field, int minWidth, int maxWidth, SignStyle signStyle, int subsequentWidth) { // validated by caller this.field = field; this.minWidth = minWidth; @@ -2343,6 +2504,9 @@ public final class DateTimeFormatterBuilder { * @return a new updated printer-parser, not null */ NumberPrinterParser withFixedWidth() { + if (subsequentWidth == -1) { + return this; + } return new NumberPrinterParser(field, minWidth, maxWidth, signStyle, -1); } @@ -2363,24 +2527,24 @@ public final class DateTimeFormatterBuilder { return false; } long value = getValue(valueLong); - DateTimeFormatSymbols symbols = context.getSymbols(); + DecimalStyle decimalStyle = context.getDecimalStyle(); String str = (value == Long.MIN_VALUE ? "9223372036854775808" : Long.toString(Math.abs(value))); if (str.length() > maxWidth) { throw new DateTimeException("Field " + field.getName() + " cannot be printed as the value " + value + " exceeds the maximum print width of " + maxWidth); } - str = symbols.convertNumberToI18N(str); + str = decimalStyle.convertNumberToI18N(str); if (value >= 0) { switch (signStyle) { case EXCEEDS_PAD: if (minWidth < 19 && value >= EXCEED_POINTS[minWidth]) { - buf.append(symbols.getPositiveSign()); + buf.append(decimalStyle.getPositiveSign()); } break; case ALWAYS: - buf.append(symbols.getPositiveSign()); + buf.append(decimalStyle.getPositiveSign()); break; } } else { @@ -2388,7 +2552,7 @@ public final class DateTimeFormatterBuilder { case NORMAL: case EXCEEDS_PAD: case ALWAYS: - buf.append(symbols.getNegativeSign()); + buf.append(decimalStyle.getNegativeSign()); break; case NOT_NEGATIVE: throw new DateTimeException("Field " + field.getName() + @@ -2397,7 +2561,7 @@ public final class DateTimeFormatterBuilder { } } for (int i = 0; i < minWidth - str.length(); i++) { - buf.append(symbols.getZeroDigit()); + buf.append(decimalStyle.getZeroDigit()); } buf.append(str); return true; @@ -2426,13 +2590,13 @@ public final class DateTimeFormatterBuilder { char sign = text.charAt(position); // IOOBE if invalid position boolean negative = false; boolean positive = false; - if (sign == context.getSymbols().getPositiveSign()) { + if (sign == context.getDecimalStyle().getPositiveSign()) { if (signStyle.parse(true, context.isStrict(), minWidth == maxWidth) == false) { return ~position; } positive = true; position++; - } else if (sign == context.getSymbols().getNegativeSign()) { + } else if (sign == context.getDecimalStyle().getNegativeSign()) { if (signStyle.parse(false, context.isStrict(), minWidth == maxWidth) == false) { return ~position; } @@ -2448,7 +2612,7 @@ public final class DateTimeFormatterBuilder { if (minEndPos > length) { return ~position; } - int effMaxWidth = maxWidth + Math.max(subsequentWidth, 0); + int effMaxWidth = (context.isStrict() || isFixedWidth() ? maxWidth : 9) + Math.max(subsequentWidth, 0); long total = 0; BigInteger totalBig = null; int pos = position; @@ -2456,7 +2620,7 @@ public final class DateTimeFormatterBuilder { int maxEndPos = Math.min(pos + effMaxWidth, length); while (pos < maxEndPos) { char ch = text.charAt(pos++); - int digit = context.getSymbols().convertToDigit(ch); + int digit = context.getDecimalStyle().convertToDigit(ch); if (digit < 0) { pos--; if (pos < minEndPos) { @@ -2550,62 +2714,110 @@ public final class DateTimeFormatterBuilder { */ static final class ReducedPrinterParser extends NumberPrinterParser { private final int baseValue; - private final int range; /** * Constructor. * * @param field the field to format, validated not null - * @param width the field width, from 1 to 18 + * @param minWidth the minimum field width, from 1 to 10 + * @param maxWidth the maximum field width, from 1 to 10 * @param baseValue the base value */ - ReducedPrinterParser(TemporalField field, int width, int baseValue) { - super(field, width, width, SignStyle.NOT_NEGATIVE); - if (width < 1 || width > 18) { - throw new IllegalArgumentException("The width must be from 1 to 18 inclusive but was " + width); + ReducedPrinterParser(TemporalField field, int minWidth, int maxWidth, + int baseValue) { + this(field, minWidth, maxWidth, baseValue, 0); + if (minWidth < 1 || minWidth > 10) { + throw new IllegalArgumentException("The minWidth must be from 1 to 10 inclusive but was " + minWidth); + } + if (maxWidth < 1 || maxWidth > 10) { + throw new IllegalArgumentException("The maxWidth must be from 1 to 10 inclusive but was " + minWidth); + } + if (maxWidth < minWidth) { + throw new IllegalArgumentException("Maximum width must exceed or equal the minimum width but " + + maxWidth + " < " + minWidth); } if (field.range().isValidValue(baseValue) == false) { throw new IllegalArgumentException("The base value must be within the range of the field"); } - this.baseValue = baseValue; - this.range = EXCEED_POINTS[width]; - if ((((long) baseValue) + range) > Integer.MAX_VALUE) { + if ((((long) baseValue) + EXCEED_POINTS[maxWidth]) > Integer.MAX_VALUE) { throw new DateTimeException("Unable to add printer-parser as the range exceeds the capacity of an int"); } } + /** + * Constructor. + * The arguments have already been checked. + * + * @param field the field to format, validated not null + * @param minWidth the minimum field width, from 1 to 10 + * @param maxWidth the maximum field width, from 1 to 10 + * @param baseValue the base value + * @param subsequentWidth the subsequentWidth for this instance + */ + private ReducedPrinterParser(TemporalField field, int minWidth, int maxWidth, + int baseValue, int subsequentWidth) { + super(field, minWidth, maxWidth, SignStyle.NOT_NEGATIVE, subsequentWidth); + this.baseValue = baseValue; + } + @Override long getValue(long value) { - return Math.abs(value % range); + long absValue = Math.abs(value); + if (value >= baseValue && value < baseValue + EXCEED_POINTS[minWidth]) { + // Use the reduced value if it fits in minWidth + return absValue % EXCEED_POINTS[minWidth]; + } + // Otherwise truncate to fit in maxWidth + return absValue % EXCEED_POINTS[maxWidth]; } @Override int setValue(DateTimeParseContext context, long value, int errorPos, int successPos) { - int lastPart = baseValue % range; - if (baseValue > 0) { - value = baseValue - lastPart + value; - } else { - value = baseValue - lastPart - value; - } - if (value < baseValue) { - value += range; + int parseLen = successPos - errorPos; + if (parseLen == minWidth && value >= 0) { + long range = EXCEED_POINTS[minWidth]; + long lastPart = baseValue % range; + long basePart = baseValue - lastPart; + if (baseValue > 0) { + value = basePart + value; + } else { + value = basePart - value; + } + if (basePart != 0 && value < baseValue) { + value += range; + } } return context.setParsedField(field, value, errorPos, successPos); } + /** + * Returns a new instance with fixed width flag set. + * + * @return a new updated printer-parser, not null + */ @Override - NumberPrinterParser withFixedWidth() { - return this; + ReducedPrinterParser withFixedWidth() { + if (subsequentWidth == -1) { + return this; + } + return new ReducedPrinterParser(field, minWidth, maxWidth, baseValue, -1); } + /** + * Returns a new instance with an updated subsequent width. + * + * @param subsequentWidth the width of subsequent non-negative numbers, 0 or greater + * @return a new updated printer-parser, not null + */ @Override - boolean isFixedWidth() { - return true; + ReducedPrinterParser withSubsequentWidth(int subsequentWidth) { + return new ReducedPrinterParser(field, minWidth, maxWidth, baseValue, + this.subsequentWidth + subsequentWidth); } @Override public String toString() { - return "ReducedValue(" + field.getName() + "," + minWidth + "," + baseValue + ")"; + return "ReducedValue(" + field.getName() + "," + minWidth + "," + maxWidth + "," + baseValue + ")"; } } @@ -2654,24 +2866,24 @@ public final class DateTimeFormatterBuilder { if (value == null) { return false; } - DateTimeFormatSymbols symbols = context.getSymbols(); + DecimalStyle decimalStyle = context.getDecimalStyle(); BigDecimal fraction = convertToFraction(value); if (fraction.scale() == 0) { // scale is zero if value is zero if (minWidth > 0) { if (decimalPoint) { - buf.append(symbols.getDecimalSeparator()); + buf.append(decimalStyle.getDecimalSeparator()); } for (int i = 0; i < minWidth; i++) { - buf.append(symbols.getZeroDigit()); + buf.append(decimalStyle.getZeroDigit()); } } } else { int outputScale = Math.min(Math.max(fraction.scale(), minWidth), maxWidth); fraction = fraction.setScale(outputScale, RoundingMode.FLOOR); String str = fraction.toPlainString().substring(2); - str = symbols.convertNumberToI18N(str); + str = decimalStyle.convertNumberToI18N(str); if (decimalPoint) { - buf.append(symbols.getDecimalSeparator()); + buf.append(decimalStyle.getDecimalSeparator()); } buf.append(str); } @@ -2688,7 +2900,7 @@ public final class DateTimeFormatterBuilder { return (effectiveMin > 0 ? ~position : position); } if (decimalPoint) { - if (text.charAt(position) != context.getSymbols().getDecimalSeparator()) { + if (text.charAt(position) != context.getDecimalStyle().getDecimalSeparator()) { // valid if whole field is optional, invalid if minimum width return (effectiveMin > 0 ? ~position : position); } @@ -2703,7 +2915,7 @@ public final class DateTimeFormatterBuilder { int pos = position; while (pos < maxEndPos) { char ch = text.charAt(pos++); - int digit = context.getSymbols().convertToDigit(ch); + int digit = context.getDecimalStyle().convertToDigit(ch); if (digit < 0) { if (pos < minEndPos) { return ~position; // need at least min width digits @@ -2883,43 +3095,50 @@ public final class DateTimeFormatterBuilder { // seconds per day = 86400 private static final long SECONDS_PER_10000_YEARS = 146097L * 25L * 86400L; private static final long SECONDS_0000_TO_1970 = ((146097L * 5L) - (30L * 365L + 7L)) * 86400L; - private static final CompositePrinterParser PARSER = new DateTimeFormatterBuilder() - .parseCaseInsensitive() - .append(DateTimeFormatter.ISO_LOCAL_DATE).appendLiteral('T') - .append(DateTimeFormatter.ISO_LOCAL_TIME).appendLiteral('Z') - .toFormatter().toPrinterParser(false); + private final int fractionalDigits; - InstantPrinterParser() { + InstantPrinterParser(int fractionalDigits) { + this.fractionalDigits = fractionalDigits; } @Override public boolean format(DateTimePrintContext context, StringBuilder buf) { // use INSTANT_SECONDS, thus this code is not bound by Instant.MAX Long inSecs = context.getValue(INSTANT_SECONDS); - Long inNanos = context.getValue(NANO_OF_SECOND); - if (inSecs == null || inNanos == null) { + Long inNanos = null; + if (context.getTemporal().isSupported(NANO_OF_SECOND)) { + inNanos = context.getTemporal().getLong(NANO_OF_SECOND); + } + if (inSecs == null) { return false; } long inSec = inSecs; - int inNano = NANO_OF_SECOND.checkValidIntValue(inNanos); + int inNano = NANO_OF_SECOND.checkValidIntValue(inNanos != null ? inNanos : 0); + // format mostly using LocalDateTime.toString if (inSec >= -SECONDS_0000_TO_1970) { // current era long zeroSecs = inSec - SECONDS_PER_10000_YEARS + SECONDS_0000_TO_1970; long hi = Math.floorDiv(zeroSecs, SECONDS_PER_10000_YEARS) + 1; long lo = Math.floorMod(zeroSecs, SECONDS_PER_10000_YEARS); - LocalDateTime ldt = LocalDateTime.ofEpochSecond(lo - SECONDS_0000_TO_1970, inNano, ZoneOffset.UTC); + LocalDateTime ldt = LocalDateTime.ofEpochSecond(lo - SECONDS_0000_TO_1970, 0, ZoneOffset.UTC); if (hi > 0) { buf.append('+').append(hi); } - buf.append(ldt).append('Z'); + buf.append(ldt); + if (ldt.getSecond() == 0) { + buf.append(":00"); + } } else { // before current era long zeroSecs = inSec + SECONDS_0000_TO_1970; long hi = zeroSecs / SECONDS_PER_10000_YEARS; long lo = zeroSecs % SECONDS_PER_10000_YEARS; - LocalDateTime ldt = LocalDateTime.ofEpochSecond(lo - SECONDS_0000_TO_1970, inNano, ZoneOffset.UTC); + LocalDateTime ldt = LocalDateTime.ofEpochSecond(lo - SECONDS_0000_TO_1970, 0, ZoneOffset.UTC); int pos = buf.length(); - buf.append(ldt).append('Z'); + buf.append(ldt); + if (ldt.getSecond() == 0) { + buf.append(":00"); + } if (hi < 0) { if (ldt.getYear() == -10_000) { buf.replace(pos, pos + 2, Long.toString(hi - 1)); @@ -2930,14 +3149,38 @@ public final class DateTimeFormatterBuilder { } } } + // add fraction + if ((fractionalDigits < 0 && inNano > 0) || fractionalDigits > 0) { + buf.append('.'); + int div = 100_000_000; + for (int i = 0; ((fractionalDigits == -1 && inNano > 0) || + (fractionalDigits == -2 && (inNano > 0 || (i % 3) != 0)) || + i < fractionalDigits); i++) { + int digit = inNano / div; + buf.append((char) (digit + '0')); + inNano = inNano - (digit * div); + div = div / 10; + } + } + buf.append('Z'); return true; } @Override public int parse(DateTimeParseContext context, CharSequence text, int position) { // new context to avoid overwriting fields like year/month/day + int minDigits = (fractionalDigits < 0 ? 0 : fractionalDigits); + int maxDigits = (fractionalDigits < 0 ? 9 : fractionalDigits); + CompositePrinterParser parser = new DateTimeFormatterBuilder() + .append(DateTimeFormatter.ISO_LOCAL_DATE).appendLiteral('T') + .appendValue(HOUR_OF_DAY, 2).appendLiteral(':') + .appendValue(MINUTE_OF_HOUR, 2).appendLiteral(':') + .appendValue(SECOND_OF_MINUTE, 2) + .appendFraction(NANO_OF_SECOND, minDigits, maxDigits, true) + .appendLiteral('Z') + .toFormatter().toPrinterParser(false); DateTimeParseContext newContext = context.copy(); - int pos = PARSER.parse(newContext, text, position); + int pos = parser.parse(newContext, text, position); if (pos < 0) { return pos; } @@ -2952,10 +3195,18 @@ public final class DateTimeFormatterBuilder { Long nanoVal = newContext.getParsed(NANO_OF_SECOND); int sec = (secVal != null ? secVal.intValue() : 0); int nano = (nanoVal != null ? nanoVal.intValue() : 0); + int days = 0; + if (hour == 24 && min == 0 && sec == 0 && nano == 0) { + hour = 0; + days = 1; + } else if (hour == 23 && min == 59 && sec == 60) { + context.setParsedLeapSecond(); + sec = 59; + } int year = (int) yearParsed % 10_000; long instantSecs; try { - LocalDateTime ldt = LocalDateTime.of(year, month, day, hour, min, sec, 0); + LocalDateTime ldt = LocalDateTime.of(year, month, day, hour, min, sec, 0).plusDays(days); instantSecs = ldt.toEpochSecond(ZoneOffset.UTC); instantSecs += Math.multiplyExact(yearParsed / 10_000L, SECONDS_PER_10000_YEARS); } catch (RuntimeException ex) { @@ -4017,9 +4268,7 @@ public final class DateTimeFormatterBuilder { String key = chrono.getId() + '|' + locale.toString() + '|' + dateStyle + timeStyle; DateTimeFormatter formatter = FORMATTER_CACHE.get(key); if (formatter == null) { - LocaleResources lr = LocaleProviderAdapter.getResourceBundleBased().getLocaleResources(locale); - String pattern = lr.getJavaTimeDateTimePattern( - convertStyle(timeStyle), convertStyle(dateStyle), chrono.getCalendarType()); + String pattern = getLocalizedDateTimePattern(dateStyle, timeStyle, chrono, locale); formatter = new DateTimeFormatterBuilder().appendPattern(pattern).toFormatter(locale); DateTimeFormatter old = FORMATTER_CACHE.putIfAbsent(key, formatter); if (old != null) { @@ -4029,19 +4278,6 @@ public final class DateTimeFormatterBuilder { return formatter; } - /** - * Converts the given FormatStyle to the java.text.DateFormat style. - * - * @param style the FormatStyle style - * @return the int style, or -1 if style is null, indicating unrequired - */ - private int convertStyle(FormatStyle style) { - if (style == null) { - return -1; - } - return style.ordinal(); // indices happen to align - } - @Override public String toString() { return "Localized(" + (dateStyle != null ? dateStyle : "") + "," + @@ -4096,7 +4332,7 @@ public final class DateTimeFormatterBuilder { case 'Y': field = weekDef.weekBasedYear(); if (count == 2) { - return new ReducedPrinterParser(field, 2, 2000); + return new ReducedPrinterParser(field, 2, 2, 2000, 0); } else { return new NumberPrinterParser(field, count, 19, (count < 4) ? SignStyle.NORMAL : SignStyle.EXCEEDS_PAD, -1); diff --git a/jdk/src/share/classes/java/time/format/DateTimeParseContext.java b/jdk/src/share/classes/java/time/format/DateTimeParseContext.java index f2e3ff3a300..f72ef7b77c9 100644 --- a/jdk/src/share/classes/java/time/format/DateTimeParseContext.java +++ b/jdk/src/share/classes/java/time/format/DateTimeParseContext.java @@ -61,6 +61,7 @@ */ package java.time.format; +import java.time.Duration; import java.time.ZoneId; import java.time.chrono.Chronology; import java.time.chrono.IsoChronology; @@ -79,7 +80,7 @@ import java.util.Objects; * Once parsing is complete, the {@link #toParsed()} is used to obtain the data. * It contains a method to resolve the separate parsed fields into meaningful values. * - *

      Specification for implementors

      + * @implSpec * This class is a mutable context intended for use from a single thread. * Usage of the class is thread-safe within standard parsing as a new instance of this class * is automatically created for each parse and parsing is single-threaded @@ -118,9 +119,13 @@ final class DateTimeParseContext { /** * Creates a copy of this context. + * This retains the case sensitive and strict flags. */ DateTimeParseContext copy() { - return new DateTimeParseContext(formatter); + DateTimeParseContext newContext = new DateTimeParseContext(formatter); + newContext.caseSensitive = caseSensitive; + newContext.strict = strict; + return newContext; } //----------------------------------------------------------------------- @@ -128,7 +133,7 @@ final class DateTimeParseContext { * Gets the locale. *

      * This locale is used to control localization in the parse except - * where localization is controlled by the symbols. + * where localization is controlled by the DecimalStyle. * * @return the locale, not null */ @@ -137,14 +142,14 @@ final class DateTimeParseContext { } /** - * Gets the formatting symbols. + * Gets the DecimalStyle. *

      - * The symbols control the localization of numeric parsing. + * The DecimalStyle controls the numeric parsing. * - * @return the formatting symbols, not null + * @return the DecimalStyle, not null */ - DateTimeFormatSymbols getSymbols() { - return formatter.getSymbols(); + DecimalStyle getDecimalStyle() { + return formatter.getDecimalStyle(); } /** @@ -370,6 +375,13 @@ final class DateTimeParseContext { currentParsed().zone = zone; } + /** + * Stores the parsed leap second. + */ + void setParsedLeapSecond() { + currentParsed().leapSecond = true; + } + //----------------------------------------------------------------------- /** * Returns a string version of the context for debugging. diff --git a/jdk/src/share/classes/java/time/format/DateTimeParseException.java b/jdk/src/share/classes/java/time/format/DateTimeParseException.java index 37b72a2f92a..d02ed15a046 100644 --- a/jdk/src/share/classes/java/time/format/DateTimeParseException.java +++ b/jdk/src/share/classes/java/time/format/DateTimeParseException.java @@ -68,7 +68,7 @@ import java.time.DateTimeException; *

      * This exception includes the text being parsed and the error index. * - *

      Specification for implementors

      + * @implSpec * This class is intended for use in a single thread. * * @since 1.8 diff --git a/jdk/src/share/classes/java/time/format/DateTimePrintContext.java b/jdk/src/share/classes/java/time/format/DateTimePrintContext.java index 8124fc85c98..3e3f90fb5eb 100644 --- a/jdk/src/share/classes/java/time/format/DateTimePrintContext.java +++ b/jdk/src/share/classes/java/time/format/DateTimePrintContext.java @@ -85,7 +85,7 @@ import java.util.Objects; *

      * This class provides a single wrapper to items used in the format. * - *

      Specification for implementors

      + * @implSpec * This class is a mutable context intended for use from a single thread. * Usage of the class is thread-safe within standard printing as the framework creates * a new instance of the class for each format and printing is single-threaded. @@ -234,7 +234,7 @@ final class DateTimePrintContext { * Gets the locale. *

      * This locale is used to control localization in the format output except - * where localization is controlled by the symbols. + * where localization is controlled by the DecimalStyle. * * @return the locale, not null */ @@ -243,14 +243,14 @@ final class DateTimePrintContext { } /** - * Gets the formatting symbols. + * Gets the DecimalStyle. *

      - * The symbols control the localization of numeric output. + * The DecimalStyle controls the localization of numeric output. * - * @return the formatting symbols, not null + * @return the DecimalStyle, not null */ - DateTimeFormatSymbols getSymbols() { - return formatter.getSymbols(); + DecimalStyle getDecimalStyle() { + return formatter.getDecimalStyle(); } //----------------------------------------------------------------------- diff --git a/jdk/src/share/classes/java/time/format/DateTimeTextProvider.java b/jdk/src/share/classes/java/time/format/DateTimeTextProvider.java index 7262c5b3341..4e1bd4a811f 100644 --- a/jdk/src/share/classes/java/time/format/DateTimeTextProvider.java +++ b/jdk/src/share/classes/java/time/format/DateTimeTextProvider.java @@ -94,7 +94,7 @@ import sun.util.locale.provider.LocaleResources; /** * A provider to obtain the textual form of a date-time field. * - *

      Specification for implementors

      + * @implSpec * Implementations must be thread-safe. * Implementations should cache the textual information. * diff --git a/jdk/src/share/classes/java/time/format/DateTimeFormatSymbols.java b/jdk/src/share/classes/java/time/format/DecimalStyle.java similarity index 82% rename from jdk/src/share/classes/java/time/format/DateTimeFormatSymbols.java rename to jdk/src/share/classes/java/time/format/DecimalStyle.java index 0bfe8441020..8143037feff 100644 --- a/jdk/src/share/classes/java/time/format/DateTimeFormatSymbols.java +++ b/jdk/src/share/classes/java/time/format/DecimalStyle.java @@ -62,34 +62,38 @@ package java.time.format; import java.text.DecimalFormatSymbols; +import java.util.Arrays; +import java.util.Collections; +import java.util.HashSet; import java.util.Locale; import java.util.Objects; +import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentMap; /** - * Localized symbols used in date and time formatting. + * Localized decimal style used in date and time formatting. *

      * A significant part of dealing with dates and times is the localization. * This class acts as a central point for accessing the information. * - *

      Specification for implementors

      + * @implSpec * This class is immutable and thread-safe. * * @since 1.8 */ -public final class DateTimeFormatSymbols { +public final class DecimalStyle { /** - * The standard set of non-localized symbols. + * The standard set of non-localized decimal style symbols. *

      * This uses standard ASCII characters for zero, positive, negative and a dot for the decimal point. */ - public static final DateTimeFormatSymbols STANDARD = new DateTimeFormatSymbols('0', '+', '-', '.'); + public static final DecimalStyle STANDARD = new DecimalStyle('0', '+', '-', '.'); /** - * The cache of symbols instances. + * The cache of DecimalStyle instances. */ - private static final ConcurrentMap CACHE = new ConcurrentHashMap<>(16, 0.75f, 2); + private static final ConcurrentMap CACHE = new ConcurrentHashMap<>(16, 0.75f, 2); /** * The zero digit. @@ -114,17 +118,20 @@ public final class DateTimeFormatSymbols { *

      * The locale 'en_US' will always be present. * - * @return an array of locales for which localization is supported + * @return a Set of Locales for which localization is supported */ - public static Locale[] getAvailableLocales() { - return DecimalFormatSymbols.getAvailableLocales(); + public static Set getAvailableLocales() { + Locale[] l = DecimalFormatSymbols.getAvailableLocales(); + Set locales = new HashSet<>(l.length); + Collections.addAll(locales, l); + return locales; } /** - * Obtains symbols for the default + * Obtains the DecimalStyle for the default * {@link java.util.Locale.Category#FORMAT FORMAT} locale. *

      - * This method provides access to locale sensitive symbols. + * This method provides access to locale sensitive decimal style symbols. *

      * This is equivalent to calling * {@link #of(Locale) @@ -133,21 +140,21 @@ public final class DateTimeFormatSymbols { * @see java.util.Locale.Category#FORMAT * @return the info, not null */ - public static DateTimeFormatSymbols ofDefaultLocale() { + public static DecimalStyle ofDefaultLocale() { return of(Locale.getDefault(Locale.Category.FORMAT)); } /** - * Obtains symbols for the specified locale. + * Obtains the DecimalStyle for the specified locale. *

      - * This method provides access to locale sensitive symbols. + * This method provides access to locale sensitive decimal style symbols. * * @param locale the locale, not null * @return the info, not null */ - public static DateTimeFormatSymbols of(Locale locale) { + public static DecimalStyle of(Locale locale) { Objects.requireNonNull(locale, "locale"); - DateTimeFormatSymbols info = CACHE.get(locale); + DecimalStyle info = CACHE.get(locale); if (info == null) { info = create(locale); CACHE.putIfAbsent(locale, info); @@ -156,7 +163,7 @@ public final class DateTimeFormatSymbols { return info; } - private static DateTimeFormatSymbols create(Locale locale) { + private static DecimalStyle create(Locale locale) { DecimalFormatSymbols oldSymbols = DecimalFormatSymbols.getInstance(locale); char zeroDigit = oldSymbols.getZeroDigit(); char positiveSign = '+'; @@ -165,7 +172,7 @@ public final class DateTimeFormatSymbols { if (zeroDigit == '0' && negativeSign == '-' && decimalSeparator == '.') { return STANDARD; } - return new DateTimeFormatSymbols(zeroDigit, positiveSign, negativeSign, decimalSeparator); + return new DecimalStyle(zeroDigit, positiveSign, negativeSign, decimalSeparator); } //----------------------------------------------------------------------- @@ -177,7 +184,7 @@ public final class DateTimeFormatSymbols { * @param negativeSignChar the character to use for the negative sign * @param decimalPointChar the character to use for the decimal point */ - private DateTimeFormatSymbols(char zeroChar, char positiveSignChar, char negativeSignChar, char decimalPointChar) { + private DecimalStyle(char zeroChar, char positiveSignChar, char negativeSignChar, char decimalPointChar) { this.zeroDigit = zeroChar; this.positiveSign = positiveSignChar; this.negativeSign = negativeSignChar; @@ -207,11 +214,11 @@ public final class DateTimeFormatSymbols { * @return a copy with a new character that represents zero, not null */ - public DateTimeFormatSymbols withZeroDigit(char zeroDigit) { + public DecimalStyle withZeroDigit(char zeroDigit) { if (zeroDigit == this.zeroDigit) { return this; } - return new DateTimeFormatSymbols(zeroDigit, positiveSign, negativeSign, decimalSeparator); + return new DecimalStyle(zeroDigit, positiveSign, negativeSign, decimalSeparator); } //----------------------------------------------------------------------- @@ -236,11 +243,11 @@ public final class DateTimeFormatSymbols { * @param positiveSign the character for the positive sign * @return a copy with a new character that represents the positive sign, not null */ - public DateTimeFormatSymbols withPositiveSign(char positiveSign) { + public DecimalStyle withPositiveSign(char positiveSign) { if (positiveSign == this.positiveSign) { return this; } - return new DateTimeFormatSymbols(zeroDigit, positiveSign, negativeSign, decimalSeparator); + return new DecimalStyle(zeroDigit, positiveSign, negativeSign, decimalSeparator); } //----------------------------------------------------------------------- @@ -265,11 +272,11 @@ public final class DateTimeFormatSymbols { * @param negativeSign the character for the negative sign * @return a copy with a new character that represents the negative sign, not null */ - public DateTimeFormatSymbols withNegativeSign(char negativeSign) { + public DecimalStyle withNegativeSign(char negativeSign) { if (negativeSign == this.negativeSign) { return this; } - return new DateTimeFormatSymbols(zeroDigit, positiveSign, negativeSign, decimalSeparator); + return new DecimalStyle(zeroDigit, positiveSign, negativeSign, decimalSeparator); } //----------------------------------------------------------------------- @@ -294,11 +301,11 @@ public final class DateTimeFormatSymbols { * @param decimalSeparator the character for the decimal point * @return a copy with a new character that represents the decimal point, not null */ - public DateTimeFormatSymbols withDecimalSeparator(char decimalSeparator) { + public DecimalStyle withDecimalSeparator(char decimalSeparator) { if (decimalSeparator == this.decimalSeparator) { return this; } - return new DateTimeFormatSymbols(zeroDigit, positiveSign, negativeSign, decimalSeparator); + return new DecimalStyle(zeroDigit, positiveSign, negativeSign, decimalSeparator); } //----------------------------------------------------------------------- @@ -333,7 +340,7 @@ public final class DateTimeFormatSymbols { //----------------------------------------------------------------------- /** - * Checks if these symbols equal another set of symbols. + * Checks if this DecimalStyle is equal another DecimalStyle. * * @param obj the object to check, null returns false * @return true if this is equal to the other date @@ -343,8 +350,8 @@ public final class DateTimeFormatSymbols { if (this == obj) { return true; } - if (obj instanceof DateTimeFormatSymbols) { - DateTimeFormatSymbols other = (DateTimeFormatSymbols) obj; + if (obj instanceof DecimalStyle) { + DecimalStyle other = (DecimalStyle) obj; return (zeroDigit == other.zeroDigit && positiveSign == other.positiveSign && negativeSign == other.negativeSign && decimalSeparator == other.decimalSeparator); } @@ -352,7 +359,7 @@ public final class DateTimeFormatSymbols { } /** - * A hash code for these symbols. + * A hash code for this DecimalStyle. * * @return a suitable hash code */ @@ -363,13 +370,13 @@ public final class DateTimeFormatSymbols { //----------------------------------------------------------------------- /** - * Returns a string describing these symbols. + * Returns a string describing this DecimalStyle. * * @return a string description, not null */ @Override public String toString() { - return "Symbols[" + zeroDigit + positiveSign + negativeSign + decimalSeparator + "]"; + return "DecimalStyle[" + zeroDigit + positiveSign + negativeSign + decimalSeparator + "]"; } } diff --git a/jdk/src/share/classes/java/time/format/FormatStyle.java b/jdk/src/share/classes/java/time/format/FormatStyle.java index 0be538e28dd..a5c82295405 100644 --- a/jdk/src/share/classes/java/time/format/FormatStyle.java +++ b/jdk/src/share/classes/java/time/format/FormatStyle.java @@ -67,7 +67,7 @@ package java.time.format; * These styles are used when obtaining a date-time style from configuration. * See {@link DateTimeFormatter} and {@link DateTimeFormatterBuilder} for usage. * - *

      Specification for implementors

      + * @implSpec * This is an immutable and thread-safe enum. * * @since 1.8 diff --git a/jdk/src/share/classes/java/time/format/Parsed.java b/jdk/src/share/classes/java/time/format/Parsed.java index a35fab1b9d8..b0a7fe810d9 100644 --- a/jdk/src/share/classes/java/time/format/Parsed.java +++ b/jdk/src/share/classes/java/time/format/Parsed.java @@ -80,6 +80,7 @@ import static java.time.temporal.ChronoField.SECOND_OF_MINUTE; import java.time.DateTimeException; import java.time.LocalDate; import java.time.LocalTime; +import java.time.Period; import java.time.ZoneId; import java.time.chrono.ChronoLocalDate; import java.time.chrono.Chronology; @@ -105,7 +106,7 @@ import java.util.Set; * Once parsing is completed, this class can be used as the resultant {@code TemporalAccessor}. * In most cases, it is only exposed once the fields have been resolved. * - *

      Specification for implementors

      + * @implSpec * This class is a mutable context intended for use from a single thread. * Usage of the class is thread-safe within standard parsing as a new instance of this class * is automatically created for each parse and parsing is single-threaded @@ -127,6 +128,10 @@ final class Parsed implements TemporalAccessor { * The parsed chronology. */ Chronology chrono; + /** + * Whether a leap-second is parsed. + */ + boolean leapSecond; /** * The effective chronology. */ @@ -143,6 +148,10 @@ final class Parsed implements TemporalAccessor { * The resolved time. */ private LocalTime time; + /** + * The excess period from time-only parsing. + */ + Period excessDays = Period.ZERO; /** * Creates an instance. @@ -159,6 +168,7 @@ final class Parsed implements TemporalAccessor { cloned.fieldValues.putAll(this.fieldValues); cloned.zone = this.zone; cloned.chrono = this.chrono; + cloned.leapSecond = this.leapSecond; return cloned; } @@ -232,6 +242,7 @@ final class Parsed implements TemporalAccessor { resolveFields(); resolveTimeLenient(); crossCheck(); + resolvePeriod(); return this; } @@ -308,36 +319,72 @@ final class Parsed implements TemporalAccessor { private void resolveTimeFields() { // simplify fields if (fieldValues.containsKey(CLOCK_HOUR_OF_DAY)) { + // lenient allows anything, smart allows 0-24, strict allows 1-24 long ch = fieldValues.remove(CLOCK_HOUR_OF_DAY); + if (resolverStyle == ResolverStyle.STRICT || (resolverStyle == ResolverStyle.SMART && ch != 0)) { + CLOCK_HOUR_OF_DAY.checkValidValue(ch); + } updateCheckConflict(CLOCK_HOUR_OF_DAY, HOUR_OF_DAY, ch == 24 ? 0 : ch); } if (fieldValues.containsKey(CLOCK_HOUR_OF_AMPM)) { + // lenient allows anything, smart allows 0-12, strict allows 1-12 long ch = fieldValues.remove(CLOCK_HOUR_OF_AMPM); + if (resolverStyle == ResolverStyle.STRICT || (resolverStyle == ResolverStyle.SMART && ch != 0)) { + CLOCK_HOUR_OF_AMPM.checkValidValue(ch); + } updateCheckConflict(CLOCK_HOUR_OF_AMPM, HOUR_OF_AMPM, ch == 12 ? 0 : ch); } if (fieldValues.containsKey(AMPM_OF_DAY) && fieldValues.containsKey(HOUR_OF_AMPM)) { long ap = fieldValues.remove(AMPM_OF_DAY); long hap = fieldValues.remove(HOUR_OF_AMPM); - updateCheckConflict(AMPM_OF_DAY, HOUR_OF_DAY, ap * 12 + hap); + if (resolverStyle == ResolverStyle.LENIENT) { + updateCheckConflict(AMPM_OF_DAY, HOUR_OF_DAY, Math.addExact(Math.multiplyExact(ap, 12), hap)); + } else { // STRICT or SMART + AMPM_OF_DAY.checkValidValue(ap); + HOUR_OF_AMPM.checkValidValue(ap); + updateCheckConflict(AMPM_OF_DAY, HOUR_OF_DAY, ap * 12 + hap); + } + } + if (fieldValues.containsKey(NANO_OF_DAY)) { + long nod = fieldValues.remove(NANO_OF_DAY); + if (resolverStyle != ResolverStyle.LENIENT) { + NANO_OF_DAY.checkValidValue(nod); + } + updateCheckConflict(NANO_OF_DAY, HOUR_OF_DAY, nod / 3600_000_000_000L); + updateCheckConflict(NANO_OF_DAY, MINUTE_OF_HOUR, (nod / 60_000_000_000L) % 60); + updateCheckConflict(NANO_OF_DAY, SECOND_OF_MINUTE, (nod / 1_000_000_000L) % 60); + updateCheckConflict(NANO_OF_DAY, NANO_OF_SECOND, nod % 1_000_000_000L); } if (fieldValues.containsKey(MICRO_OF_DAY)) { long cod = fieldValues.remove(MICRO_OF_DAY); + if (resolverStyle != ResolverStyle.LENIENT) { + MICRO_OF_DAY.checkValidValue(cod); + } updateCheckConflict(MICRO_OF_DAY, SECOND_OF_DAY, cod / 1_000_000L); updateCheckConflict(MICRO_OF_DAY, MICRO_OF_SECOND, cod % 1_000_000L); } if (fieldValues.containsKey(MILLI_OF_DAY)) { long lod = fieldValues.remove(MILLI_OF_DAY); + if (resolverStyle != ResolverStyle.LENIENT) { + MILLI_OF_DAY.checkValidValue(lod); + } updateCheckConflict(MILLI_OF_DAY, SECOND_OF_DAY, lod / 1_000); updateCheckConflict(MILLI_OF_DAY, MILLI_OF_SECOND, lod % 1_000); } if (fieldValues.containsKey(SECOND_OF_DAY)) { long sod = fieldValues.remove(SECOND_OF_DAY); + if (resolverStyle != ResolverStyle.LENIENT) { + SECOND_OF_DAY.checkValidValue(sod); + } updateCheckConflict(SECOND_OF_DAY, HOUR_OF_DAY, sod / 3600); updateCheckConflict(SECOND_OF_DAY, MINUTE_OF_HOUR, (sod / 60) % 60); updateCheckConflict(SECOND_OF_DAY, SECOND_OF_MINUTE, sod % 60); } if (fieldValues.containsKey(MINUTE_OF_DAY)) { long mod = fieldValues.remove(MINUTE_OF_DAY); + if (resolverStyle != ResolverStyle.LENIENT) { + MINUTE_OF_DAY.checkValidValue(mod); + } updateCheckConflict(MINUTE_OF_DAY, HOUR_OF_DAY, mod / 60); updateCheckConflict(MINUTE_OF_DAY, MINUTE_OF_HOUR, mod % 60); } @@ -345,29 +392,34 @@ final class Parsed implements TemporalAccessor { // combine partial second fields strictly, leaving lenient expansion to later if (fieldValues.containsKey(NANO_OF_SECOND)) { long nos = fieldValues.get(NANO_OF_SECOND); + if (resolverStyle != ResolverStyle.LENIENT) { + NANO_OF_SECOND.checkValidValue(nos); + } if (fieldValues.containsKey(MICRO_OF_SECOND)) { long cos = fieldValues.remove(MICRO_OF_SECOND); + if (resolverStyle != ResolverStyle.LENIENT) { + MICRO_OF_SECOND.checkValidValue(cos); + } nos = cos * 1000 + (nos % 1000); updateCheckConflict(MICRO_OF_SECOND, NANO_OF_SECOND, nos); } if (fieldValues.containsKey(MILLI_OF_SECOND)) { long los = fieldValues.remove(MILLI_OF_SECOND); + if (resolverStyle != ResolverStyle.LENIENT) { + MILLI_OF_SECOND.checkValidValue(los); + } updateCheckConflict(MILLI_OF_SECOND, NANO_OF_SECOND, los * 1_000_000L + (nos % 1_000_000L)); } } - // convert to time if possible - if (fieldValues.containsKey(NANO_OF_DAY)) { - long nod = fieldValues.remove(NANO_OF_DAY); - updateCheckConflict(LocalTime.ofNanoOfDay(nod)); - } + // convert to time if all four fields available (optimization) if (fieldValues.containsKey(HOUR_OF_DAY) && fieldValues.containsKey(MINUTE_OF_HOUR) && fieldValues.containsKey(SECOND_OF_MINUTE) && fieldValues.containsKey(NANO_OF_SECOND)) { - int hodVal = HOUR_OF_DAY.checkValidIntValue(fieldValues.remove(HOUR_OF_DAY)); - int mohVal = MINUTE_OF_HOUR.checkValidIntValue(fieldValues.remove(MINUTE_OF_HOUR)); - int somVal = SECOND_OF_MINUTE.checkValidIntValue(fieldValues.remove(SECOND_OF_MINUTE)); - int nosVal = NANO_OF_SECOND.checkValidIntValue(fieldValues.remove(NANO_OF_SECOND)); - updateCheckConflict(LocalTime.of(hodVal, mohVal, somVal, nosVal)); + long hod = fieldValues.remove(HOUR_OF_DAY); + long moh = fieldValues.remove(MINUTE_OF_HOUR); + long som = fieldValues.remove(SECOND_OF_MINUTE); + long nos = fieldValues.remove(NANO_OF_SECOND); + resolveTime(hod, moh, som, nos); } } @@ -377,7 +429,7 @@ final class Parsed implements TemporalAccessor { // which would break updateCheckConflict(field) if (time == null) { - // can only get here if NANO_OF_SECOND not present + // NANO_OF_SECOND merged with MILLI/MICRO above if (fieldValues.containsKey(MILLI_OF_SECOND)) { long los = fieldValues.remove(MILLI_OF_SECOND); if (fieldValues.containsKey(MICRO_OF_SECOND)) { @@ -395,43 +447,87 @@ final class Parsed implements TemporalAccessor { long cos = fieldValues.remove(MICRO_OF_SECOND); fieldValues.put(NANO_OF_SECOND, cos * 1_000L); } - } - // merge hour/minute/second/nano leniently - Long hod = fieldValues.get(HOUR_OF_DAY); - if (hod != null) { - int hodVal = HOUR_OF_DAY.checkValidIntValue(hod); - Long moh = fieldValues.get(MINUTE_OF_HOUR); - Long som = fieldValues.get(SECOND_OF_MINUTE); - Long nos = fieldValues.get(NANO_OF_SECOND); + // merge hour/minute/second/nano leniently + Long hod = fieldValues.get(HOUR_OF_DAY); + if (hod != null) { + Long moh = fieldValues.get(MINUTE_OF_HOUR); + Long som = fieldValues.get(SECOND_OF_MINUTE); + Long nos = fieldValues.get(NANO_OF_SECOND); - // check for invalid combinations that cannot be defaulted - if (time == null) { + // check for invalid combinations that cannot be defaulted if ((moh == null && (som != null || nos != null)) || (moh != null && som == null && nos != null)) { return; } - } - // default as necessary and build time - int mohVal = (moh != null ? MINUTE_OF_HOUR.checkValidIntValue(moh) : (time != null ? time.getMinute() : 0)); - int somVal = (som != null ? SECOND_OF_MINUTE.checkValidIntValue(som) : (time != null ? time.getSecond() : 0)); - int nosVal = (nos != null ? NANO_OF_SECOND.checkValidIntValue(nos) : (time != null ? time.getNano() : 0)); - updateCheckConflict(LocalTime.of(hodVal, mohVal, somVal, nosVal)); - fieldValues.remove(HOUR_OF_DAY); - fieldValues.remove(MINUTE_OF_HOUR); - fieldValues.remove(SECOND_OF_MINUTE); - fieldValues.remove(NANO_OF_SECOND); + // default as necessary and build time + long mohVal = (moh != null ? moh : 0); + long somVal = (som != null ? som : 0); + long nosVal = (nos != null ? nos : 0); + resolveTime(hod, mohVal, somVal, nosVal); + fieldValues.remove(HOUR_OF_DAY); + fieldValues.remove(MINUTE_OF_HOUR); + fieldValues.remove(SECOND_OF_MINUTE); + fieldValues.remove(NANO_OF_SECOND); + } + } + + // validate remaining + if (resolverStyle != ResolverStyle.LENIENT && fieldValues.size() > 0) { + for (Entry entry : fieldValues.entrySet()) { + TemporalField field = entry.getKey(); + if (field instanceof ChronoField && field.isTimeBased()) { + ((ChronoField) field).checkValidValue(entry.getValue()); + } + } } } - private void updateCheckConflict(LocalTime lt) { + private void resolveTime(long hod, long moh, long som, long nos) { + if (resolverStyle == ResolverStyle.LENIENT) { + long totalNanos = Math.multiplyExact(hod, 3600_000_000_000L); + totalNanos = Math.addExact(totalNanos, Math.multiplyExact(moh, 60_000_000_000L)); + totalNanos = Math.addExact(totalNanos, Math.multiplyExact(som, 1_000_000_000L)); + totalNanos = Math.addExact(totalNanos, nos); + int excessDays = (int) Math.floorDiv(totalNanos, 86400_000_000_000L); // safe int cast + long nod = Math.floorMod(totalNanos, 86400_000_000_000L); + updateCheckConflict(LocalTime.ofNanoOfDay(nod), Period.ofDays(excessDays)); + } else { // STRICT or SMART + int mohVal = MINUTE_OF_HOUR.checkValidIntValue(moh); + int nosVal = NANO_OF_SECOND.checkValidIntValue(nos); + // handle 24:00 end of day + if (resolverStyle == ResolverStyle.SMART && hod == 24 && mohVal == 0 && som == 0 && nosVal == 0) { + updateCheckConflict(LocalTime.MIDNIGHT, Period.ofDays(1)); + } else { + int hodVal = HOUR_OF_DAY.checkValidIntValue(hod); + int somVal = SECOND_OF_MINUTE.checkValidIntValue(som); + updateCheckConflict(LocalTime.of(hodVal, mohVal, somVal, nosVal), Period.ZERO); + } + } + } + + private void resolvePeriod() { + // add whole days if we have both date and time + if (date != null && time != null && excessDays.isZero() == false) { + date = date.plus(excessDays); + excessDays = Period.ZERO; + } + } + + private void updateCheckConflict(LocalTime timeToSet, Period periodToSet) { if (time != null) { - if (lt != null && time.equals(lt) == false) { - throw new DateTimeException("Conflict found: Fields resolved to two different times: " + time + " " + lt); + if (time.equals(timeToSet) == false) { + throw new DateTimeException("Conflict found: Fields resolved to different times: " + time + " " + timeToSet); + } + if (excessDays.isZero() == false && periodToSet.isZero() == false && excessDays.equals(periodToSet) == false) { + throw new DateTimeException("Conflict found: Fields resolved to different excess periods: " + excessDays + " " + periodToSet); + } else { + excessDays = periodToSet; } } else { - time = lt; + time = timeToSet; + excessDays = periodToSet; } } diff --git a/jdk/src/share/classes/java/time/format/ResolverStyle.java b/jdk/src/share/classes/java/time/format/ResolverStyle.java index b53b827ab8d..313bd178d84 100644 --- a/jdk/src/share/classes/java/time/format/ResolverStyle.java +++ b/jdk/src/share/classes/java/time/format/ResolverStyle.java @@ -69,7 +69,7 @@ package java.time.format; * Phase 2 resolves the parsed field-value pairs into date and/or time objects. * This style is used to control how phase 2, resolving, happens. * - *

      Specification for implementors

      + * @implSpec * This is an immutable and thread-safe enum. * * @since 1.8 @@ -96,10 +96,9 @@ public enum ResolverStyle { * behavior. Individual fields will interpret this differently. *

      * For example, resolving year-month and day-of-month in the ISO calendar - * system using smart mode will ensure that the day-of-month is valid - * for the year-month, rejecting invalid values, with the exception that - * February 29th in a year other than a leap year will be converted to - * February 28th. + * system using smart mode will ensure that the day-of-month is from + * 1 to 31, converting any value beyond the last valid day-of-month to be + * the last valid day-of-month. */ SMART, /** @@ -110,6 +109,7 @@ public enum ResolverStyle { *

      * For example, lenient mode allows the month in the ISO calendar system * to be outside the range 1 to 12. + * For example, month 15 is treated as being 3 months after month 12. */ LENIENT; diff --git a/jdk/src/share/classes/java/time/format/SignStyle.java b/jdk/src/share/classes/java/time/format/SignStyle.java index 2f8c57aecb3..56dfd03511a 100644 --- a/jdk/src/share/classes/java/time/format/SignStyle.java +++ b/jdk/src/share/classes/java/time/format/SignStyle.java @@ -68,7 +68,7 @@ package java.time.format; * to be controlled using this enum. * See {@link DateTimeFormatterBuilder} for usage. * - *

      Specification for implementors

      + * @implSpec * This is an immutable and thread-safe enum. * * @since 1.8 diff --git a/jdk/src/share/classes/java/time/format/TextStyle.java b/jdk/src/share/classes/java/time/format/TextStyle.java index dbc2c27f0d0..ed65d2773a3 100644 --- a/jdk/src/share/classes/java/time/format/TextStyle.java +++ b/jdk/src/share/classes/java/time/format/TextStyle.java @@ -80,7 +80,7 @@ import java.util.Calendar; * For example, the word used for a month when used alone in a date picker is different * to the word used for month in association with a day and year in a date. * - *

      Specification for implementors

      + * @implSpec * This is immutable and thread-safe enum. */ public enum TextStyle { diff --git a/jdk/src/share/classes/java/time/format/package-info.java b/jdk/src/share/classes/java/time/format/package-info.java index 94a0600dc8e..140bb6d750d 100644 --- a/jdk/src/share/classes/java/time/format/package-info.java +++ b/jdk/src/share/classes/java/time/format/package-info.java @@ -76,7 +76,7 @@ * Localization occurs by calling * {@link java.time.format.DateTimeFormatter#withLocale(java.util.Locale) withLocale(Locale)} * on the formatter. Further customization is possible using - * {@link java.time.format.DateTimeFormatSymbols DateTimeFormatSymbols}. + * {@link java.time.format.DecimalStyle DecimalStyle}. *

      * *

      Package specification

      diff --git a/jdk/src/share/classes/java/time/temporal/ChronoField.java b/jdk/src/share/classes/java/time/temporal/ChronoField.java index 0fdeb2273d5..ac4522bb60b 100644 --- a/jdk/src/share/classes/java/time/temporal/ChronoField.java +++ b/jdk/src/share/classes/java/time/temporal/ChronoField.java @@ -93,7 +93,7 @@ import sun.util.locale.provider.LocaleResources; * just with slightly different rules. * The documentation of each field explains how it operates. * - *

      Specification for implementors

      + * @implSpec * This is a final, immutable and thread-safe enum. * * @since 1.8 @@ -115,6 +115,10 @@ public enum ChronoField implements TemporalField { * object stores, using integer division to remove excess precision. * For example, if the {@code TemporalAccessor} stores time to millisecond precision, * then the nano-of-second must be divided by 1,000,000 before replacing the milli-of-second. + *

      + * When parsing this field it behaves equivalent to the following: + * The value is validated in strict and smart mode but not in lenient mode. + * The field is resolved in combination with {@code MILLI_OF_SECOND} and {@code MICRO_OF_SECOND}. */ NANO_OF_SECOND("NanoOfSecond", NANOS, SECONDS, ValueRange.of(0, 999_999_999)), /** @@ -126,6 +130,11 @@ public enum ChronoField implements TemporalField { * This field is used to represent the nano-of-day handling any fraction of the second. * Implementations of {@code TemporalAccessor} should provide a value for this field if * they can return a value for {@link #SECOND_OF_DAY} filling unknown precision with zero. + *

      + * When parsing this field it behaves equivalent to the following: + * The value is validated in strict and smart mode but not in lenient mode. + * The value is split to form {@code NANO_OF_SECOND}, {@code SECOND_OF_MINUTE}, + * {@code MINUTE_OF_HOUR} and {@code HOUR_OF_DAY} fields. */ NANO_OF_DAY("NanoOfDay", NANOS, DAYS, ValueRange.of(0, 86400L * 1000_000_000L - 1)), /** @@ -141,6 +150,11 @@ public enum ChronoField implements TemporalField { *

      * When this field is used for setting a value, it should behave in the same way as * setting {@link #NANO_OF_SECOND} with the value multiplied by 1,000. + *

      + * When parsing this field it behaves equivalent to the following: + * The value is validated in strict and smart mode but not in lenient mode. + * The field is resolved in combination with {@code MILLI_OF_SECOND} to produce + * {@code NANO_OF_SECOND}. */ MICRO_OF_SECOND("MicroOfSecond", MICROS, SECONDS, ValueRange.of(0, 999_999)), /** @@ -155,6 +169,11 @@ public enum ChronoField implements TemporalField { *

      * When this field is used for setting a value, it should behave in the same way as * setting {@link #NANO_OF_DAY} with the value multiplied by 1,000. + *

      + * When parsing this field it behaves equivalent to the following: + * The value is validated in strict and smart mode but not in lenient mode. + * The value is split to form {@code MICRO_OF_SECOND}, {@code SECOND_OF_MINUTE}, + * {@code MINUTE_OF_HOUR} and {@code HOUR_OF_DAY} fields. */ MICRO_OF_DAY("MicroOfDay", MICROS, DAYS, ValueRange.of(0, 86400L * 1000_000L - 1)), /** @@ -170,6 +189,11 @@ public enum ChronoField implements TemporalField { *

      * When this field is used for setting a value, it should behave in the same way as * setting {@link #NANO_OF_SECOND} with the value multiplied by 1,000,000. + *

      + * When parsing this field it behaves equivalent to the following: + * The value is validated in strict and smart mode but not in lenient mode. + * The field is resolved in combination with {@code MICRO_OF_SECOND} to produce + * {@code NANO_OF_SECOND}. */ MILLI_OF_SECOND("MilliOfSecond", MILLIS, SECONDS, ValueRange.of(0, 999)), /** @@ -184,6 +208,11 @@ public enum ChronoField implements TemporalField { *

      * When this field is used for setting a value, it should behave in the same way as * setting {@link #NANO_OF_DAY} with the value multiplied by 1,000,000. + *

      + * When parsing this field it behaves equivalent to the following: + * The value is validated in strict and smart mode but not in lenient mode. + * The value is split to form {@code MILLI_OF_SECOND}, {@code SECOND_OF_MINUTE}, + * {@code MINUTE_OF_HOUR} and {@code HOUR_OF_DAY} fields. */ MILLI_OF_DAY("MilliOfDay", MILLIS, DAYS, ValueRange.of(0, 86400L * 1000L - 1)), /** @@ -191,6 +220,9 @@ public enum ChronoField implements TemporalField { *

      * This counts the second within the minute, from 0 to 59. * This field has the same meaning for all calendar systems. + *

      + * When parsing this field it behaves equivalent to the following: + * The value is validated in strict and smart mode but not in lenient mode. */ SECOND_OF_MINUTE("SecondOfMinute", SECONDS, MINUTES, ValueRange.of(0, 59), "second"), /** @@ -198,6 +230,11 @@ public enum ChronoField implements TemporalField { *

      * This counts the second within the day, from 0 to (24 * 60 * 60) - 1. * This field has the same meaning for all calendar systems. + *

      + * When parsing this field it behaves equivalent to the following: + * The value is validated in strict and smart mode but not in lenient mode. + * The value is split to form {@code SECOND_OF_MINUTE}, {@code MINUTE_OF_HOUR} + * and {@code HOUR_OF_DAY} fields. */ SECOND_OF_DAY("SecondOfDay", SECONDS, DAYS, ValueRange.of(0, 86400L - 1)), /** @@ -205,6 +242,9 @@ public enum ChronoField implements TemporalField { *

      * This counts the minute within the hour, from 0 to 59. * This field has the same meaning for all calendar systems. + *

      + * When parsing this field it behaves equivalent to the following: + * The value is validated in strict and smart mode but not in lenient mode. */ MINUTE_OF_HOUR("MinuteOfHour", MINUTES, HOURS, ValueRange.of(0, 59), "minute"), /** @@ -212,6 +252,10 @@ public enum ChronoField implements TemporalField { *

      * This counts the minute within the day, from 0 to (24 * 60) - 1. * This field has the same meaning for all calendar systems. + *

      + * When parsing this field it behaves equivalent to the following: + * The value is validated in strict and smart mode but not in lenient mode. + * The value is split to form {@code MINUTE_OF_HOUR} and {@code HOUR_OF_DAY} fields. */ MINUTE_OF_DAY("MinuteOfDay", MINUTES, DAYS, ValueRange.of(0, (24 * 60) - 1)), /** @@ -220,6 +264,12 @@ public enum ChronoField implements TemporalField { * This counts the hour within the AM/PM, from 0 to 11. * This is the hour that would be observed on a standard 12-hour digital clock. * This field has the same meaning for all calendar systems. + *

      + * When parsing this field it behaves equivalent to the following: + * The value is validated from 0 to 11 in strict and smart mode. + * In lenient mode the value is not validated. It is combined with + * {@code AMPM_OF_DAY} to form {@code HOUR_OF_DAY} by multiplying + * the {AMPM_OF_DAY} value by 12. */ HOUR_OF_AMPM("HourOfAmPm", HOURS, HALF_DAYS, ValueRange.of(0, 11)), /** @@ -228,6 +278,12 @@ public enum ChronoField implements TemporalField { * This counts the hour within the AM/PM, from 1 to 12. * This is the hour that would be observed on a standard 12-hour analog wall clock. * This field has the same meaning for all calendar systems. + *

      + * When parsing this field it behaves equivalent to the following: + * The value is validated from 1 to 12 in strict mode and from + * 0 to 12 in smart mode. In lenient mode the value is not validated. + * The field is converted to an {@code HOUR_OF_AMPM} with the same value, + * unless the value is 12, in which case it is converted to 0. */ CLOCK_HOUR_OF_AMPM("ClockHourOfAmPm", HOURS, HALF_DAYS, ValueRange.of(1, 12)), /** @@ -236,6 +292,13 @@ public enum ChronoField implements TemporalField { * This counts the hour within the day, from 0 to 23. * This is the hour that would be observed on a standard 24-hour digital clock. * This field has the same meaning for all calendar systems. + *

      + * When parsing this field it behaves equivalent to the following: + * The value is validated in strict and smart mode but not in lenient mode. + * The field is combined with {@code MINUTE_OF_HOUR}, {@code SECOND_OF_MINUTE} and + * {@code NANO_OF_SECOND} to produce a {@code LocalTime}. + * In lenient mode, any excess days are added to the parsed date, or + * made available via {@link java.time.format.DateTimeFormatter#parsedExcessDays()}. */ HOUR_OF_DAY("HourOfDay", HOURS, DAYS, ValueRange.of(0, 23), "hour"), /** @@ -244,6 +307,12 @@ public enum ChronoField implements TemporalField { * This counts the hour within the AM/PM, from 1 to 24. * This is the hour that would be observed on a 24-hour analog wall clock. * This field has the same meaning for all calendar systems. + *

      + * When parsing this field it behaves equivalent to the following: + * The value is validated from 1 to 24 in strict mode and from + * 0 to 24 in smart mode. In lenient mode the value is not validated. + * The field is converted to an {@code HOUR_OF_DAY} with the same value, + * unless the value is 24, in which case it is converted to 0. */ CLOCK_HOUR_OF_DAY("ClockHourOfDay", HOURS, DAYS, ValueRange.of(1, 24)), /** @@ -251,6 +320,12 @@ public enum ChronoField implements TemporalField { *

      * This counts the AM/PM within the day, from 0 (AM) to 1 (PM). * This field has the same meaning for all calendar systems. + *

      + * When parsing this field it behaves equivalent to the following: + * The value is validated from 0 to 1 in strict and smart mode. + * In lenient mode the value is not validated. It is combined with + * {@code HOUR_OF_AMPM} to form {@code HOUR_OF_DAY} by multiplying + * the {AMPM_OF_DAY} value by 12. */ AMPM_OF_DAY("AmPmOfDay", HALF_DAYS, DAYS, ValueRange.of(0, 1), "dayperiod"), /** diff --git a/jdk/src/share/classes/java/time/temporal/ChronoUnit.java b/jdk/src/share/classes/java/time/temporal/ChronoUnit.java index 03c5310312d..661960c28fb 100644 --- a/jdk/src/share/classes/java/time/temporal/ChronoUnit.java +++ b/jdk/src/share/classes/java/time/temporal/ChronoUnit.java @@ -72,7 +72,7 @@ import java.time.chrono.ChronoZonedDateTime; * just with slightly different rules. * The documentation of each unit explains how it operates. * - *

      Specification for implementors

      + * @implSpec * This is a final, immutable and thread-safe enum. * * @since 1.8 diff --git a/jdk/src/share/classes/java/time/temporal/IsoFields.java b/jdk/src/share/classes/java/time/temporal/IsoFields.java index 851685a3132..e335c434d23 100644 --- a/jdk/src/share/classes/java/time/temporal/IsoFields.java +++ b/jdk/src/share/classes/java/time/temporal/IsoFields.java @@ -146,7 +146,7 @@ import sun.util.locale.provider.LocaleResources; * 2009-01-05MondayWeek 2 of week-based-year 2009 * * - *

      Specification for implementors

      + * @implSpec *

      * This class is immutable and thread-safe. * diff --git a/jdk/src/share/classes/java/time/temporal/JulianFields.java b/jdk/src/share/classes/java/time/temporal/JulianFields.java index 72956cec79a..95400addcaa 100644 --- a/jdk/src/share/classes/java/time/temporal/JulianFields.java +++ b/jdk/src/share/classes/java/time/temporal/JulianFields.java @@ -81,7 +81,7 @@ import java.util.Map; * The fields are supported, and can be queried and set if {@code EPOCH_DAY} is available. * The fields work with all chronologies. * - *

      Specification for implementors

      + * @implSpec * This is an immutable and thread-safe class. * * @since 1.8 diff --git a/jdk/src/share/classes/java/time/temporal/Temporal.java b/jdk/src/share/classes/java/time/temporal/Temporal.java index 7cbc49fa176..1928186b285 100644 --- a/jdk/src/share/classes/java/time/temporal/Temporal.java +++ b/jdk/src/share/classes/java/time/temporal/Temporal.java @@ -119,7 +119,7 @@ import java.time.ZoneId; * days to months. *

      * - *

      Specification for implementors

      + * @implSpec * This interface places no restrictions on the mutability of implementations, * however immutability is strongly recommended. * All implementations must be {@link Comparable}. @@ -146,7 +146,7 @@ public interface Temporal extends TemporalAccessor { * date = date.with(next(WEDNESDAY)); // static import from Adjusters and DayOfWeek * * - *

      Specification for implementors

      + * @implSpec * Implementations must not alter either this object. * Instead, an adjusted copy of the original must be returned. * This provides equivalent, safe behavior for immutable and mutable implementations. @@ -177,7 +177,7 @@ public interface Temporal extends TemporalAccessor { * In cases like this, the field is responsible for resolving the result. Typically it will choose * the previous valid date, which would be the last valid day of February in this example. * - *

      Specification for implementors

      + * @implSpec * Implementations must check and handle all fields defined in {@link ChronoField}. * If the field is supported, then the adjustment must be performed. * If unsupported, then an {@code UnsupportedTemporalTypeException} must be thrown. @@ -217,7 +217,7 @@ public interface Temporal extends TemporalAccessor { * Note that calling {@code plus} followed by {@code minus} is not guaranteed to * return the same date-time. * - *

      Specification for implementors

      + * @implSpec * Implementations must not alter either this object. * Instead, an adjusted copy of the original must be returned. * This provides equivalent, safe behavior for immutable and mutable implementations. @@ -247,12 +247,8 @@ public interface Temporal extends TemporalAccessor { * a date representing the 31st January, then adding one month would be unclear. * In cases like this, the field is responsible for resolving the result. Typically it will choose * the previous valid date, which would be the last valid day of February in this example. - *

      - * If the implementation represents a date-time that has boundaries, such as {@code LocalTime}, - * then the permitted units must include the boundary unit, but no multiples of the boundary unit. - * For example, {@code LocalTime} must accept {@code DAYS} but not {@code WEEKS} or {@code MONTHS}. * - *

      Specification for implementors

      + * @implSpec * Implementations must check and handle all units defined in {@link ChronoUnit}. * If the unit is supported, then the addition must be performed. * If unsupported, then an {@code UnsupportedTemporalTypeException} must be thrown. @@ -292,7 +288,7 @@ public interface Temporal extends TemporalAccessor { * Note that calling {@code plus} followed by {@code minus} is not guaranteed to * return the same date-time. * - *

      Specification for implementors

      + * @implSpec * Implementations must not alter either this object. * Instead, an adjusted copy of the original must be returned. * This provides equivalent, safe behavior for immutable and mutable implementations. @@ -322,12 +318,8 @@ public interface Temporal extends TemporalAccessor { * a date representing the 31st March, then subtracting one month would be unclear. * In cases like this, the field is responsible for resolving the result. Typically it will choose * the previous valid date, which would be the last valid day of February in this example. - *

      - * If the implementation represents a date-time that has boundaries, such as {@code LocalTime}, - * then the permitted units must include the boundary unit, but no multiples of the boundary unit. - * For example, {@code LocalTime} must accept {@code DAYS} but not {@code WEEKS} or {@code MONTHS}. * - *

      Specification for implementors

      + * @implSpec * Implementations must behave in a manor equivalent to the default method behavior. *

      * Implementations must not alter either this object or the specified temporal object. @@ -353,10 +345,10 @@ public interface Temporal extends TemporalAccessor { //----------------------------------------------------------------------- /** - * Calculates the period between this temporal and another temporal in - * terms of the specified unit. + * Calculates the amount of time until another temporal in terms of the specified unit. *

      - * This calculates the period between two temporals in terms of a single unit. + * This calculates the amount of time between two temporal objects + * of the same type in terms of a single {@code TemporalUnit}. * The start and end points are {@code this} and the specified temporal. * The result will be negative if the end is before the start. * For example, the period in hours between two temporal objects can be @@ -385,7 +377,7 @@ public interface Temporal extends TemporalAccessor { * long daysBetween = DAYS.between(start, end); * * - *

      Specification for implementors

      + * @implSpec * Implementations must begin by checking to ensure that the input temporal * object is of the same observable type as the implementation. * They must then perform the calculation for all instances of {@link ChronoUnit}. @@ -410,11 +402,11 @@ public interface Temporal extends TemporalAccessor { * Neither this object, nor the specified temporal, may be altered. * * @param endTemporal the end temporal, of the same type as this object, not null - * @param unit the unit to measure the period in, not null - * @return the period between this temporal object and the specified one in terms of - * the unit; positive if the specified object is later than this one, negative if - * it is earlier than this one - * @throws DateTimeException if the period cannot be calculated + * @param unit the unit to measure the amount in, not null + * @return the amount of time between this temporal object and the specified one + * in terms of the unit; positive if the specified object is later than this one, + * negative if it is earlier than this one + * @throws DateTimeException if the amount cannot be calculated * @throws UnsupportedTemporalTypeException if the unit is not supported * @throws ArithmeticException if numeric overflow occurs */ diff --git a/jdk/src/share/classes/java/time/temporal/TemporalAccessor.java b/jdk/src/share/classes/java/time/temporal/TemporalAccessor.java index 3f4a571e477..a212be85981 100644 --- a/jdk/src/share/classes/java/time/temporal/TemporalAccessor.java +++ b/jdk/src/share/classes/java/time/temporal/TemporalAccessor.java @@ -94,7 +94,7 @@ import java.util.Objects; * of this interface may be in calendar systems other than ISO. * See {@link java.time.chrono.ChronoLocalDate} for a fuller discussion of the issues. * - *

      Specification for implementors

      + * @implSpec * This interface places no restrictions on the mutability of implementations, * however immutability is strongly recommended. * @@ -109,7 +109,7 @@ public interface TemporalAccessor { * If false, then calling the {@link #range(TemporalField) range} and {@link #get(TemporalField) get} * methods will throw an exception. * - *

      Specification for implementors

      + * @implSpec * Implementations must check and handle all fields defined in {@link ChronoField}. * If the field is supported, then true is returned, otherwise false *

      @@ -137,7 +137,7 @@ public interface TemporalAccessor { * and it is important not to read too much into them. For example, there * could be values within the range that are invalid for the field. * - *

      Specification for implementors

      + * @implSpec * Implementations must check and handle all fields defined in {@link ChronoField}. * If the field is supported, then the range of the field must be returned. * If unsupported, then an {@code UnsupportedTemporalTypeException} must be thrown. @@ -183,7 +183,7 @@ public interface TemporalAccessor { * If the date-time cannot return the value, because the field is unsupported or for * some other reason, an exception will be thrown. * - *

      Specification for implementors

      + * @implSpec * Implementations must check and handle all fields defined in {@link ChronoField}. * If the field is supported and has an {@code int} range, then the value of * the field must be returned. @@ -231,7 +231,7 @@ public interface TemporalAccessor { * If the date-time cannot return the value, because the field is unsupported or for * some other reason, an exception will be thrown. * - *

      Specification for implementors

      + * @implSpec * Implementations must check and handle all fields defined in {@link ChronoField}. * If the field is supported, then the value of the field must be returned. * If unsupported, then an {@code UnsupportedTemporalTypeException} must be thrown. @@ -265,7 +265,7 @@ public interface TemporalAccessor { * {@code LocalDate::from} and {@code ZoneId::from}. * Additional implementations are provided as static methods on {@link TemporalQuery}. * - *

      Specification for implementors

      + * @implSpec * The default implementation must behave equivalent to this code: *
            *  if (query == TemporalQuery.zoneId() ||
      diff --git a/jdk/src/share/classes/java/time/temporal/TemporalAdjuster.java b/jdk/src/share/classes/java/time/temporal/TemporalAdjuster.java
      index 0cd44a363d2..13f8cea80d6 100644
      --- a/jdk/src/share/classes/java/time/temporal/TemporalAdjuster.java
      +++ b/jdk/src/share/classes/java/time/temporal/TemporalAdjuster.java
      @@ -97,7 +97,7 @@ import java.util.function.UnaryOperator;
        * 
    4. finding the next or previous day-of-week, such as "next Thursday" * * - *

      Specification for implementors

      + * @implSpec * This interface places no restrictions on the mutability of implementations, * however immutability is strongly recommended. *

      @@ -127,7 +127,7 @@ public interface TemporalAdjuster { * It is recommended to use the second approach, {@code with(TemporalAdjuster)}, * as it is a lot clearer to read in code. * - *

      Specification for implementors

      + * @implSpec * The implementation must take the input object and adjust it. * The implementation defines the logic of the adjustment and is responsible for * documenting that logic. It may use any method on {@code Temporal} to @@ -162,10 +162,10 @@ public interface TemporalAdjuster { * This is provided for convenience to make user-written adjusters simpler. *

      * In general, user-written adjusters should be static constants: - *

      +     * 
      {@code
            *  static TemporalAdjuster TWO_DAYS_LATER = TemporalAdjuster.ofDateAdjuster(
            *    date -> date.plusDays(2));
      -     * 
      + * }
      * * @param dateBasedAdjuster the date-based adjuster, not null * @return the temporal adjuster wrapping on the date adjuster, not null diff --git a/jdk/src/share/classes/java/time/temporal/TemporalAmount.java b/jdk/src/share/classes/java/time/temporal/TemporalAmount.java index a264ebaa604..802dc4d73d5 100644 --- a/jdk/src/share/classes/java/time/temporal/TemporalAmount.java +++ b/jdk/src/share/classes/java/time/temporal/TemporalAmount.java @@ -90,7 +90,7 @@ import java.util.List; * used in application code. Instead, applications should create and pass * around instances of concrete types, such as {@code Period} and {@code Duration}. * - *

      Specification for implementors

      + * @implSpec * This interface places no restrictions on the mutability of implementations, * however immutability is strongly recommended. * @@ -104,7 +104,7 @@ public interface TemporalAmount { * value of the {@code TemporalAmount}. A value must be returned * for each unit listed in {@code getUnits}. * - *

      Specification for implementors

      + * @implSpec * Implementations may declare support for units not listed by {@link #getUnits()}. * Typically, the implementation would define additional units * as conversions for the convenience of developers. @@ -124,7 +124,7 @@ public interface TemporalAmount { * The units are ordered from longest duration to the shortest duration * of the unit. * - *

      Specification for implementors

      + * @implSpec * The list of units completely and uniquely represents the * state of the object without omissions, overlaps or duplication. * The units are in order from longest duration to shortest. @@ -150,7 +150,7 @@ public interface TemporalAmount { * It is recommended to use the second approach, {@code plus(TemporalAmount)}, * as it is a lot clearer to read in code. * - *

      Specification for implementors

      + * @implSpec * The implementation must take the input object and add to it. * The implementation defines the logic of the addition and is responsible for * documenting that logic. It may use any method on {@code Temporal} to @@ -192,7 +192,7 @@ public interface TemporalAmount { * It is recommended to use the second approach, {@code minus(TemporalAmount)}, * as it is a lot clearer to read in code. * - *

      Specification for implementors

      + * @implSpec * The implementation must take the input object and subtract from it. * The implementation defines the logic of the subtraction and is responsible for * documenting that logic. It may use any method on {@code Temporal} to diff --git a/jdk/src/share/classes/java/time/temporal/TemporalField.java b/jdk/src/share/classes/java/time/temporal/TemporalField.java index 81992b4fb2f..cce2dfbd4de 100644 --- a/jdk/src/share/classes/java/time/temporal/TemporalField.java +++ b/jdk/src/share/classes/java/time/temporal/TemporalField.java @@ -82,7 +82,7 @@ import java.util.Objects; * If it is, then the date-time must handle it. * Otherwise, the method call is re-dispatched to the matching method in this interface. * - *

      Specification for implementors

      + * @implSpec * This interface must be implemented with care to ensure other classes operate correctly. * All implementations that can be instantiated must be final, immutable and thread-safe. * Implementations should be {@code Serializable} where possible. diff --git a/jdk/src/share/classes/java/time/temporal/TemporalQuery.java b/jdk/src/share/classes/java/time/temporal/TemporalQuery.java index 599377847aa..188b13df61e 100644 --- a/jdk/src/share/classes/java/time/temporal/TemporalQuery.java +++ b/jdk/src/share/classes/java/time/temporal/TemporalQuery.java @@ -96,7 +96,7 @@ import java.time.chrono.Chronology; * {@code LocalDate::from} and {@code ZoneId::from}. * Additional common implementations are provided on this interface as static methods. * - *

      Specification for implementors

      + * @implSpec * This interface places no restrictions on the mutability of implementations, * however immutability is strongly recommended. * @@ -124,7 +124,7 @@ public interface TemporalQuery { * It is recommended to use the second approach, {@code query(TemporalQuery)}, * as it is a lot clearer to read in code. * - *

      Specification for implementors

      + * @implSpec * The implementation must take the input object and query it. * The implementation defines the logic of the query and is responsible for * documenting that logic. diff --git a/jdk/src/share/classes/java/time/temporal/TemporalUnit.java b/jdk/src/share/classes/java/time/temporal/TemporalUnit.java index 86e3ba86137..01ab406f85a 100644 --- a/jdk/src/share/classes/java/time/temporal/TemporalUnit.java +++ b/jdk/src/share/classes/java/time/temporal/TemporalUnit.java @@ -83,7 +83,7 @@ import java.time.Period; * If it is, then the date-time must handle it. * Otherwise, the method call is re-dispatched to the matching method in this interface. * - *

      Specification for implementors

      + * @implSpec * This interface must be implemented with care to ensure other classes operate correctly. * All implementations that can be instantiated must be final, immutable and thread-safe. * It is recommended to use an enum where possible. @@ -197,19 +197,17 @@ public interface TemporalUnit { //----------------------------------------------------------------------- /** - * Calculates the period in terms of this unit between two temporal objects - * of the same type. + * Calculates the amount of time between two temporal objects. *

      - * This calculates the period between two temporals in terms of this unit. - * The start and end points are supplied as temporal objects and must be - * of the same type. + * This calculates the amount in terms of this unit. The start and end + * points are supplied as temporal objects and must be of the same type. * The result will be negative if the end is before the start. - * For example, the period in hours between two temporal objects can be + * For example, the amount in hours between two temporal objects can be * calculated using {@code HOURS.between(startTime, endTime)}. *

      * The calculation returns a whole number, representing the number of * complete units between the two temporals. - * For example, the period in hours between the times 11:30 and 13:29 + * For example, the amount in hours between the times 11:30 and 13:29 * will only be one hour as it is one minute short of two hours. *

      * There are two equivalent ways of using this method. @@ -237,9 +235,9 @@ public interface TemporalUnit { * * @param temporal1 the base temporal object, not null * @param temporal2 the other temporal object, not null - * @return the period between temporal1 and temporal2 in terms of this unit; + * @return the amount of time between temporal1 and temporal2 in terms of this unit; * positive if temporal2 is later than temporal1, negative if earlier - * @throws DateTimeException if the period cannot be calculated + * @throws DateTimeException if the amount cannot be calculated * @throws UnsupportedTemporalTypeException if the unit is not supported by the temporal * @throws ArithmeticException if numeric overflow occurs */ diff --git a/jdk/src/share/classes/java/time/temporal/UnsupportedTemporalTypeException.java b/jdk/src/share/classes/java/time/temporal/UnsupportedTemporalTypeException.java index 4b47b0afcf1..e4b2b1281a3 100644 --- a/jdk/src/share/classes/java/time/temporal/UnsupportedTemporalTypeException.java +++ b/jdk/src/share/classes/java/time/temporal/UnsupportedTemporalTypeException.java @@ -67,7 +67,7 @@ import java.time.DateTimeException; * UnsupportedTemporalTypeException indicates that a ChronoField or ChronoUnit is * not supported for a Temporal class. * - *

      Specification for implementors

      + * @implSpec * This class is intended for use in a single thread. * * @since 1.8 diff --git a/jdk/src/share/classes/java/time/temporal/ValueRange.java b/jdk/src/share/classes/java/time/temporal/ValueRange.java index d6e3a525c70..4ac8e74402a 100644 --- a/jdk/src/share/classes/java/time/temporal/ValueRange.java +++ b/jdk/src/share/classes/java/time/temporal/ValueRange.java @@ -79,7 +79,7 @@ import java.time.DateTimeException; *

      * Instances of this class are not tied to a specific field. * - *

      Specification for implementors

      + * @implSpec * This class is immutable and thread-safe. * * @since 1.8 diff --git a/jdk/src/share/classes/java/time/temporal/WeekFields.java b/jdk/src/share/classes/java/time/temporal/WeekFields.java index 29a1e1bf8fb..07ffa91d2a3 100644 --- a/jdk/src/share/classes/java/time/temporal/WeekFields.java +++ b/jdk/src/share/classes/java/time/temporal/WeekFields.java @@ -170,7 +170,8 @@ import sun.util.locale.provider.LocaleResources; * 2009-01-05Monday * Week 2 of 2009Week 1 of 2009 * - *

      Specification for implementors

      + * + * @implSpec * This class is immutable and thread-safe. * * @since 1.8 @@ -200,8 +201,6 @@ public final class WeekFields implements Serializable { * Note that the first week may start in the previous calendar year. * Note also that the first few days of a calendar year may be in the * week-based-year corresponding to the previous calendar year. - *

      - * This field is an immutable and thread-safe singleton. */ public static final WeekFields ISO = new WeekFields(DayOfWeek.MONDAY, 4); @@ -211,8 +210,6 @@ public final class WeekFields implements Serializable { *

      * Defined as starting on Sunday and with a minimum of 1 day in the month. * This week definition is in use in the US and other European countries. - *

      - * This field is an immutable and thread-safe singleton. */ public static final WeekFields SUNDAY_START = WeekFields.of(DayOfWeek.SUNDAY, 1); @@ -230,7 +227,7 @@ public final class WeekFields implements Serializable { * In that case, the week is set to the last week of the year * with the same day-of-week. *

      - * This field is an immutable and thread-safe singleton. + * This unit is an immutable and thread-safe singleton. */ public static final TemporalUnit WEEK_BASED_YEARS = IsoFields.WEEK_BASED_YEARS; @@ -247,22 +244,18 @@ public final class WeekFields implements Serializable { * The minimal number of days in the first week. */ private final int minimalDays; - /** * The field used to access the computed DayOfWeek. */ private transient final TemporalField dayOfWeek = ComputedDayOfField.ofDayOfWeekField(this); - /** * The field used to access the computed WeekOfMonth. */ private transient final TemporalField weekOfMonth = ComputedDayOfField.ofWeekOfMonthField(this); - /** * The field used to access the computed WeekOfYear. */ private transient final TemporalField weekOfYear = ComputedDayOfField.ofWeekOfYearField(this); - /** * The field that represents the week-of-week-based-year. *

      @@ -271,7 +264,6 @@ public final class WeekFields implements Serializable { * This unit is an immutable and thread-safe singleton. */ private transient final TemporalField weekOfWeekBasedYear = ComputedDayOfField.ofWeekOfWeekBasedYearField(this); - /** * The field that represents the week-based-year. *

      @@ -281,6 +273,7 @@ public final class WeekFields implements Serializable { */ private transient final TemporalField weekBasedYear = ComputedDayOfField.ofWeekBasedYearField(this); + //----------------------------------------------------------------------- /** * Obtains an instance of {@code WeekFields} appropriate for a locale. *

      @@ -359,8 +352,7 @@ public final class WeekFields implements Serializable { try { return WeekFields.of(firstDayOfWeek, minimalDays); } catch (IllegalArgumentException iae) { - throw new InvalidObjectException("Invalid serialized WeekFields: " - + iae.getMessage()); + throw new InvalidObjectException("Invalid serialized WeekFields: " + iae.getMessage()); } } @@ -394,21 +386,24 @@ public final class WeekFields implements Serializable { //----------------------------------------------------------------------- /** - * Returns a field to access the day of week, - * computed based on this WeekFields. + * Returns a field to access the day of week based on this {@code WeekFields}. *

      - * The days of week are numbered from 1 to 7. - * Day number 1 is the {@link #getFirstDayOfWeek() first day-of-week}. + * This is similar to {@link ChronoField#DAY_OF_WEEK} but uses values for + * the day-of-week based on this {@code WeekFields}. + * The days are numbered from 1 to 7 where the + * {@link #getFirstDayOfWeek() first day-of-week} is assigned the value 1. + *

      + * For example, if the first day-of-week is Sunday, then that will have the + * value 1, with other days ranging from Monday as 2 to Saturday as 7. * - * @return the field for day-of-week using this week definition, not null + * @return a field providing access to the day-of-week with localized numbering, not null */ public TemporalField dayOfWeek() { return dayOfWeek; } /** - * Returns a field to access the week of month, - * computed based on this WeekFields. + * Returns a field to access the week of month based on this {@code WeekFields}. *

      * This represents the concept of the count of weeks within the month where weeks * start on a fixed day-of-week, such as Monday. @@ -426,15 +421,15 @@ public final class WeekFields implements Serializable { * - if the 5th day of the month is a Monday, week two starts on the 5th and the 1st to 4th is in week one
      *

      * This field can be used with any calendar system. - * @return a TemporalField to access the WeekOfMonth, not null + * + * @return a field providing access to the week-of-month, not null */ public TemporalField weekOfMonth() { return weekOfMonth; } /** - * Returns a field to access the week of year, - * computed based on this WeekFields. + * Returns a field to access the week of year based on this {@code WeekFields}. *

      * This represents the concept of the count of weeks within the year where weeks * start on a fixed day-of-week, such as Monday. @@ -452,15 +447,15 @@ public final class WeekFields implements Serializable { * - if the 5th day of the year is a Monday, week two starts on the 5th and the 1st to 4th is in week one
      *

      * This field can be used with any calendar system. - * @return a TemporalField to access the WeekOfYear, not null + * + * @return a field providing access to the week-of-year, not null */ public TemporalField weekOfYear() { return weekOfYear; } /** - * Returns a field to access the week of a week-based-year, - * computed based on this WeekFields. + * Returns a field to access the week of a week-based-year based on this {@code WeekFields}. *

      * This represents the concept of the count of weeks within the year where weeks * start on a fixed day-of-week, such as Monday and each week belongs to exactly one year. @@ -482,15 +477,15 @@ public final class WeekFields implements Serializable { * the 1st to 4th is in week one
      *

      * This field can be used with any calendar system. - * @return a TemporalField to access the week of week-based-year, not null + * + * @return a field providing access to the week-of-week-based-year, not null */ public TemporalField weekOfWeekBasedYear() { return weekOfWeekBasedYear; } /** - * Returns a field to access the year of a week-based-year, - * computed based on this WeekFields. + * Returns a field to access the year of a week-based-year based on this {@code WeekFields}. *

      * This represents the concept of the year where weeks start on a fixed day-of-week, * such as Monday and each week belongs to exactly one year. @@ -504,14 +499,16 @@ public final class WeekFields implements Serializable { * is in the last week of the previous year. *

      * This field can be used with any calendar system. - * @return a TemporalField to access the year of week-based-year, not null + * + * @return a field providing access to the week-based-year, not null */ public TemporalField weekBasedYear() { return weekBasedYear; } + //----------------------------------------------------------------------- /** - * Checks if this WeekFields is equal to the specified object. + * Checks if this {@code WeekFields} is equal to the specified object. *

      * The comparison is based on the entire state of the rules, which is * the first day-of-week and minimal days. @@ -531,7 +528,7 @@ public final class WeekFields implements Serializable { } /** - * A hash code for these rules. + * A hash code for this {@code WeekFields}. * * @return a suitable hash code */ @@ -542,7 +539,7 @@ public final class WeekFields implements Serializable { //----------------------------------------------------------------------- /** - * A string representation of this definition. + * A string representation of this {@code WeekFields} instance. * * @return the string representation, not null */ @@ -957,7 +954,6 @@ public final class WeekFields implements Serializable { /** * Map the field range to a week range of a week year. * @param temporal the temporal - * @param field the field to get the range of * @return the ValueRange with the range adjusted to weeks. */ private ValueRange rangeWeekOfWeekBasedYear(TemporalAccessor temporal) { diff --git a/jdk/src/share/classes/java/time/zone/Ser.java b/jdk/src/share/classes/java/time/zone/Ser.java index 7d5c0c6719b..e34126436af 100644 --- a/jdk/src/share/classes/java/time/zone/Ser.java +++ b/jdk/src/share/classes/java/time/zone/Ser.java @@ -74,7 +74,7 @@ import java.time.ZoneOffset; /** * The shared serialization delegate for this package. * - *

      Implementation notes

      + * @implNote * This class is mutable and should be created once per serialization. * * @serial include diff --git a/jdk/src/share/classes/java/time/zone/ZoneOffsetTransition.java b/jdk/src/share/classes/java/time/zone/ZoneOffsetTransition.java index 1b5810b0594..f2eab7c888e 100644 --- a/jdk/src/share/classes/java/time/zone/ZoneOffsetTransition.java +++ b/jdk/src/share/classes/java/time/zone/ZoneOffsetTransition.java @@ -89,7 +89,7 @@ import java.util.Objects; * An example would be when the offset changes from {@code +04:00} to {@code +03:00}. * This might be described as 'the clocks will move back one hour tonight at 2am'. * - *

      Specification for implementors

      + * @implSpec * This class is immutable and thread-safe. * * @since 1.8 diff --git a/jdk/src/share/classes/java/time/zone/ZoneOffsetTransitionRule.java b/jdk/src/share/classes/java/time/zone/ZoneOffsetTransitionRule.java index bb1b79fb332..ad52f82de55 100644 --- a/jdk/src/share/classes/java/time/zone/ZoneOffsetTransitionRule.java +++ b/jdk/src/share/classes/java/time/zone/ZoneOffsetTransitionRule.java @@ -90,7 +90,7 @@ import java.util.Objects; *

      * These different rule types can be expressed and queried. * - *

      Specification for implementors

      + * @implSpec * This class is immutable and thread-safe. * * @since 1.8 diff --git a/jdk/src/share/classes/java/time/zone/ZoneRules.java b/jdk/src/share/classes/java/time/zone/ZoneRules.java index fa7498c858f..070ad6e8398 100644 --- a/jdk/src/share/classes/java/time/zone/ZoneRules.java +++ b/jdk/src/share/classes/java/time/zone/ZoneRules.java @@ -100,7 +100,7 @@ import java.util.concurrent.ConcurrentMap; * Applications should treat the data provided as representing the best information * available to the implementation of this rule. * - *

      Specification for implementors

      + * @implSpec * This class is immutable and thread-safe. * * @since 1.8 diff --git a/jdk/src/share/classes/java/time/zone/ZoneRulesException.java b/jdk/src/share/classes/java/time/zone/ZoneRulesException.java index e965af0dc34..2c845caf5fc 100644 --- a/jdk/src/share/classes/java/time/zone/ZoneRulesException.java +++ b/jdk/src/share/classes/java/time/zone/ZoneRulesException.java @@ -64,7 +64,7 @@ import java.time.DateTimeException; * This exception is used to indicate a problems with the configured * time-zone rules. * - *

      Specification for implementors

      + * @implSpec * This class is intended for use in a single thread. * * @since 1.8 diff --git a/jdk/src/share/classes/java/time/zone/ZoneRulesProvider.java b/jdk/src/share/classes/java/time/zone/ZoneRulesProvider.java index 5c88e3ea0cb..9523664d169 100644 --- a/jdk/src/share/classes/java/time/zone/ZoneRulesProvider.java +++ b/jdk/src/share/classes/java/time/zone/ZoneRulesProvider.java @@ -111,7 +111,7 @@ import java.util.concurrent.CopyOnWriteArrayList; * Each provider will provide the latest rules for each zone ID, but they * may also provide the history of how the rules changed. * - *

      Specification for implementors

      + * @implSpec * This interface is a service provider that can be called by multiple threads. * Implementations must be immutable and thread-safe. *

      diff --git a/jdk/src/share/classes/java/util/Base64.java b/jdk/src/share/classes/java/util/Base64.java index 62580594837..efb1459c9e8 100644 --- a/jdk/src/share/classes/java/util/Base64.java +++ b/jdk/src/share/classes/java/util/Base64.java @@ -625,7 +625,8 @@ public class Base64 { * character(s) padded), they are decoded as if followed by padding * character(s). If there is padding character present in the * final unit, the correct number of padding character(s) must be - * present, otherwise {@code IllegalArgumentException} is thrown + * present, otherwise {@code IllegalArgumentException} ( + * {@code IOException} when reading from a Base64 stream) is thrown * during decoding. * *

      Instances of {@link Decoder} class are safe for use by @@ -1306,7 +1307,12 @@ public class Base64 { return off - oldOff; } if (v == '=') { // padding byte(s) - if (nextin != 6 && nextin != 0) { + // = shiftto==18 unnecessary padding + // x= shiftto==12 invalid unit + // xx= shiftto==6 && missing last '=' + // xx=y or last is not '=' + if (nextin == 18 || nextin == 12 || + nextin == 6 && is.read() != '=') { throw new IOException("Illegal base64 ending sequence:" + nextin); } b[off++] = (byte)(bits >> (16)); diff --git a/jdk/src/share/classes/java/util/JapaneseImperialCalendar.java b/jdk/src/share/classes/java/util/JapaneseImperialCalendar.java index ed95f96e05d..e37374af15f 100644 --- a/jdk/src/share/classes/java/util/JapaneseImperialCalendar.java +++ b/jdk/src/share/classes/java/util/JapaneseImperialCalendar.java @@ -249,11 +249,14 @@ class JapaneseImperialCalendar extends Calendar { CalendarDate transitionDate = eras[i].getSinceDate(); date.setDate(transitionDate.getYear(), BaseCalendar.JANUARY, 1); long fdd = gcal.getFixedDate(date); - dayOfYear = Math.min((int)(fdd - fd), dayOfYear); + if (fd != fdd) { + dayOfYear = Math.min((int)(fd - fdd) + 1, dayOfYear); + } date.setDate(transitionDate.getYear(), BaseCalendar.DECEMBER, 31); - fdd = gcal.getFixedDate(date) + 1; - dayOfYear = Math.min((int)(fd - fdd), dayOfYear); - + fdd = gcal.getFixedDate(date); + if (fd != fdd) { + dayOfYear = Math.min((int)(fdd - fd) + 1, dayOfYear); + } LocalGregorianCalendar.Date lgd = getCalendarDate(fd - 1); int y = lgd.getYear(); // Unless the first year starts from January 1, the actual diff --git a/jdk/src/share/classes/java/util/Objects.java b/jdk/src/share/classes/java/util/Objects.java index 695f0c62eb3..e526079e37c 100644 --- a/jdk/src/share/classes/java/util/Objects.java +++ b/jdk/src/share/classes/java/util/Objects.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009, 2011, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2009, 2013, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -25,6 +25,8 @@ package java.util; +import java.util.function.Supplier; + /** * This class consists of {@code static} utility methods for operating * on objects. These utilities include {@code null}-safe or {@code @@ -262,4 +264,30 @@ public final class Objects { public static boolean nonNull(Object obj) { return obj != null; } + + /** + * Checks that the specified object reference is not {@code null} and + * throws a customized {@link NullPointerException} if it is. + * + *

      Unlike the method {@link #requireNonNull(Object, String)}, + * this method allows creation of the message to be deferred until + * after the null check is made. While this may confer a + * performance advantage in the non-null case, when deciding to + * call this method care should be taken that the costs of + * creating the message supplier are less than the cost of just + * creating the string message directly. + * + * @param obj the object reference to check for nullity + * @param messageSupplier supplier of the detail message to be + * used in the event that a {@code NullPointerException} is thrown + * @param the type of the reference + * @return {@code obj} if not {@code null} + * @throws NullPointerException if {@code obj} is {@code null} + * @since 1.8 + */ + public static T requireNonNull(T obj, Supplier messageSupplier) { + if (obj == null) + throw new NullPointerException(messageSupplier.get()); + return obj; + } } diff --git a/jdk/src/share/classes/sun/net/www/MessageHeader.java b/jdk/src/share/classes/sun/net/www/MessageHeader.java index 3a46def13b3..d6982b659e2 100644 --- a/jdk/src/share/classes/sun/net/www/MessageHeader.java +++ b/jdk/src/share/classes/sun/net/www/MessageHeader.java @@ -31,12 +31,7 @@ package sun.net.www; import java.io.*; import java.util.Collections; -import java.util.Map; -import java.util.HashMap; -import java.util.List; -import java.util.ArrayList; -import java.util.Iterator; -import java.util.NoSuchElementException; +import java.util.*; /** An RFC 844 or MIME message header. Includes methods for parsing headers from incoming streams, fetching @@ -59,6 +54,17 @@ class MessageHeader { parseHeader(is); } + /** + * Returns list of header names in a comma separated list + */ + public synchronized String getHeaderNamesInList() { + StringJoiner joiner = new StringJoiner(","); + for (int i=0; i() { + public Void run() throws IOException { + plainConnect0(); + return null; + } + } +// }, null, p -- replace line above, when limited doPriv ready + ); + } catch (PrivilegedActionException e) { + throw (IOException) e.getException(); + } + } else { + // run without additional permission + plainConnect0(); + } + } + + /** + * if the caller has a HttpURLPermission for connecting to the + * given URL, then return a SocketPermission which permits + * access to that destination. Return null otherwise. The permission + * is cached in a field (which can only be changed by redirects) + */ + SocketPermission URLtoSocketPermission(URL url) throws IOException { + + if (socketPermission != null) { + return socketPermission; + } + + SecurityManager sm = System.getSecurityManager(); + + if (sm == null) { + return null; + } + + // the permission, which we might grant + + SocketPermission newPerm = new SocketPermission( + getHostAndPort(url), "connect" + ); + + String actions = getRequestMethod()+":" + + getUserSetHeaders().getHeaderNamesInList(); + + HttpURLPermission p = new HttpURLPermission(url.toString(), actions); + try { + sm.checkPermission(p); + socketPermission = newPerm; + return socketPermission; + } catch (SecurityException e) { + // fall thru + } + return null; + } + + protected void plainConnect0() throws IOException { // try to see if request can be served from local cache if (cacheHandler != null && getUseCaches()) { try { @@ -1068,7 +1178,28 @@ public class HttpURLConnection extends java.net.HttpURLConnection { @Override public synchronized OutputStream getOutputStream() throws IOException { + connecting = true; + SocketPermission p = URLtoSocketPermission(this.url); + if (p != null) { + try { + return AccessController.doPrivileged( + new PrivilegedExceptionAction() { + public OutputStream run() throws IOException { + return getOutputStream0(); + } + } +// }, null, p -- replace line above, when limited doPriv ready + ); + } catch (PrivilegedActionException e) { + throw (IOException) e.getException(); + } + } else { + return getOutputStream0(); + } + } + + private synchronized OutputStream getOutputStream0() throws IOException { try { if (!doOutput) { throw new ProtocolException("cannot write to a URLConnection" @@ -1231,8 +1362,30 @@ public class HttpURLConnection extends java.net.HttpURLConnection { } @Override - @SuppressWarnings("empty-statement") public synchronized InputStream getInputStream() throws IOException { + connecting = true; + SocketPermission p = URLtoSocketPermission(this.url); + + if (p != null) { + try { + return AccessController.doPrivileged( + new PrivilegedExceptionAction() { + public InputStream run() throws IOException { + return getInputStream0(); + } + } +// }, null, p -- replace line above, when limited doPriv ready + ); + } catch (PrivilegedActionException e) { + throw (IOException) e.getException(); + } + } else { + return getInputStream0(); + } + } + + @SuppressWarnings("empty-statement") + private synchronized InputStream getInputStream0() throws IOException { if (!doInput) { throw new ProtocolException("Cannot read from URLConnection" @@ -2319,18 +2472,19 @@ public class HttpURLConnection extends java.net.HttpURLConnection { return false; } - int stat = getResponseCode(); + final int stat = getResponseCode(); if (stat < 300 || stat > 307 || stat == 306 || stat == HTTP_NOT_MODIFIED) { return false; } - String loc = getHeaderField("Location"); + final String loc = getHeaderField("Location"); if (loc == null) { /* this should be present - if not, we have no choice * but to go forward w/ the response we got */ return false; } + URL locUrl; try { locUrl = new URL(loc); @@ -2342,6 +2496,38 @@ public class HttpURLConnection extends java.net.HttpURLConnection { // treat loc as a relative URI to conform to popular browsers locUrl = new URL(url, loc); } + + final URL locUrl0 = locUrl; + socketPermission = null; // force recalculation + SocketPermission p = URLtoSocketPermission(locUrl); + + if (p != null) { + try { + return AccessController.doPrivileged( + new PrivilegedExceptionAction() { + public Boolean run() throws IOException { + return followRedirect0(loc, stat, locUrl0); + } + } +// }, null, p -- replace line above, when limited doPriv ready + ); + } catch (PrivilegedActionException e) { + throw (IOException) e.getException(); + } + } else { + // run without additional permission + return followRedirect0(loc, stat, locUrl); + } + } + + /* Tells us whether to follow a redirect. If so, it + * closes the connection (break any keep-alive) and + * resets the url, re-connects, and resets the request + * property. + */ + private boolean followRedirect0(String loc, int stat, URL locUrl) + throws IOException + { disconnectInternal(); if (streaming()) { throw new HttpRetryException (RETRY_MSG3, stat, loc); @@ -2753,17 +2939,24 @@ public class HttpURLConnection extends java.net.HttpURLConnection { * @param value the value to be set */ @Override - public void setRequestProperty(String key, String value) { - if (connected) + public synchronized void setRequestProperty(String key, String value) { + if (connected || connecting) throw new IllegalStateException("Already connected"); if (key == null) throw new NullPointerException ("key is null"); if (isExternalMessageHeaderAllowed(key, value)) { requests.set(key, value); + if (!key.equalsIgnoreCase("Content-Type")) { + userHeaders.set(key, value); + } } } + MessageHeader getUserSetHeaders() { + return userHeaders; + } + /** * Adds a general request property specified by a * key-value pair. This method will not overwrite @@ -2776,14 +2969,17 @@ public class HttpURLConnection extends java.net.HttpURLConnection { * @since 1.4 */ @Override - public void addRequestProperty(String key, String value) { - if (connected) + public synchronized void addRequestProperty(String key, String value) { + if (connected || connecting) throw new IllegalStateException("Already connected"); if (key == null) throw new NullPointerException ("key is null"); if (isExternalMessageHeaderAllowed(key, value)) { requests.add(key, value); + if (!key.equalsIgnoreCase("Content-Type")) { + userHeaders.add(key, value); + } } } diff --git a/jdk/src/share/classes/sun/nio/cs/ext/ISO2022_JP_2.java b/jdk/src/share/classes/sun/nio/cs/ext/ISO2022_JP_2.java index 12631577807..0389a12b825 100644 --- a/jdk/src/share/classes/sun/nio/cs/ext/ISO2022_JP_2.java +++ b/jdk/src/share/classes/sun/nio/cs/ext/ISO2022_JP_2.java @@ -47,17 +47,17 @@ public class ISO2022_JP_2 extends ISO2022_JP } public CharsetDecoder newDecoder() { - return new Decoder(this, Decoder.DEC0208, DEC0212); + return new Decoder(this, Decoder.DEC0208, CoderHolder.DEC0212); } public CharsetEncoder newEncoder() { - return new Encoder(this, Encoder.ENC0208, ENC0212, true); + return new Encoder(this, Encoder.ENC0208, CoderHolder.ENC0212, true); } - private final static DoubleByte.Decoder DEC0212 = - (DoubleByte.Decoder)new JIS_X_0212().newDecoder(); - - private final static DoubleByte.Encoder ENC0212 = - (DoubleByte.Encoder)new JIS_X_0212().newEncoder(); - + private static class CoderHolder { + final static DoubleByte.Decoder DEC0212 = + (DoubleByte.Decoder)new JIS_X_0212().newDecoder(); + final static DoubleByte.Encoder ENC0212 = + (DoubleByte.Encoder)new JIS_X_0212().newEncoder(); + } } diff --git a/jdk/src/share/classes/sun/nio/cs/ext/META-INF/services/java.nio.charset.spi.CharsetProvider b/jdk/src/share/classes/sun/nio/cs/ext/META-INF/services/java.nio.charset.spi.CharsetProvider deleted file mode 100644 index cf0949e4ca8..00000000000 --- a/jdk/src/share/classes/sun/nio/cs/ext/META-INF/services/java.nio.charset.spi.CharsetProvider +++ /dev/null @@ -1,2 +0,0 @@ -# NIO charset SPI extended charset provider -sun.nio.cs.ext.ExtendedCharsets diff --git a/jdk/src/share/classes/sun/nio/cs/ext/MSISO2022JP.java b/jdk/src/share/classes/sun/nio/cs/ext/MSISO2022JP.java index 2be71a210c7..761809aa304 100644 --- a/jdk/src/share/classes/sun/nio/cs/ext/MSISO2022JP.java +++ b/jdk/src/share/classes/sun/nio/cs/ext/MSISO2022JP.java @@ -46,16 +46,17 @@ public class MSISO2022JP extends ISO2022_JP } public CharsetDecoder newDecoder() { - return new Decoder(this, DEC0208, null); + return new Decoder(this, CoderHolder.DEC0208, null); } public CharsetEncoder newEncoder() { - return new Encoder(this, ENC0208, null, true); + return new Encoder(this, CoderHolder.ENC0208, null, true); } - private final static DoubleByte.Decoder DEC0208 = - (DoubleByte.Decoder)new JIS_X_0208_MS932().newDecoder(); - - private final static DoubleByte.Encoder ENC0208 = - (DoubleByte.Encoder)new JIS_X_0208_MS932().newEncoder(); + private static class CoderHolder { + final static DoubleByte.Decoder DEC0208 = + (DoubleByte.Decoder)new JIS_X_0208_MS932().newDecoder(); + final static DoubleByte.Encoder ENC0208 = + (DoubleByte.Encoder)new JIS_X_0208_MS932().newEncoder(); + } } diff --git a/jdk/src/share/classes/sun/util/calendar/LocalGregorianCalendar.java b/jdk/src/share/classes/sun/util/calendar/LocalGregorianCalendar.java index aa0a73b76a7..9a9b6637471 100644 --- a/jdk/src/share/classes/sun/util/calendar/LocalGregorianCalendar.java +++ b/jdk/src/share/classes/sun/util/calendar/LocalGregorianCalendar.java @@ -246,16 +246,16 @@ public class LocalGregorianCalendar extends BaseCalendar { return false; } ldate.setNormalizedYear(era.getSinceDate().getYear() + ldate.getYear() - 1); - // If it's not the last Era, validate the date. - if (era != eras[eras.length - 1]) { - Date tmp = newCalendarDate(date.getZone()); - tmp.setEra(era).setDate(date.getYear(), date.getMonth(), date.getDayOfMonth()); - normalize(tmp); - if (tmp.getEra() != era) { - return false; - } + Date tmp = newCalendarDate(date.getZone()); + tmp.setEra(era).setDate(date.getYear(), date.getMonth(), date.getDayOfMonth()); + normalize(tmp); + if (tmp.getEra() != era) { + return false; } } else { + if (date.getYear() >= eras[0].getSinceDate().getYear()) { + return false; + } ldate.setNormalizedYear(ldate.getYear()); } return super.validate(ldate); diff --git a/jdk/src/share/classes/sun/util/calendar/ZoneInfoFile.java b/jdk/src/share/classes/sun/util/calendar/ZoneInfoFile.java index b003c2eead0..60cd91911a2 100644 --- a/jdk/src/share/classes/sun/util/calendar/ZoneInfoFile.java +++ b/jdk/src/share/classes/sun/util/calendar/ZoneInfoFile.java @@ -585,12 +585,17 @@ public final class ZoneInfoFile { dstSavings = (startRule.offsetAfter - startRule.offsetBefore) * 1000; // Note: known mismatching -> Asia/Amman + // Asia/Gaza + // Asia/Hebron // ZoneInfo : startDayOfWeek=5 <= Thursday // startTime=86400000 <= 24 hours // This: startDayOfWeek=6 // startTime=0 // Below is the workaround, it probably slows down everyone a little - if (params[2] == 6 && params[3] == 0 && zoneId.equals("Asia/Amman")) { + if (params[2] == 6 && params[3] == 0 && + (zoneId.equals("Asia/Amman") || + zoneId.equals("Asia/Gaza") || + zoneId.equals("Asia/Hebron"))) { params[2] = 5; params[3] = 86400000; } diff --git a/jdk/src/share/classes/sun/util/resources/TimeZoneNames.java b/jdk/src/share/classes/sun/util/resources/TimeZoneNames.java index 59af1d215bf..974fd26d051 100644 --- a/jdk/src/share/classes/sun/util/resources/TimeZoneNames.java +++ b/jdk/src/share/classes/sun/util/resources/TimeZoneNames.java @@ -365,7 +365,7 @@ public final class TimeZoneNames extends TimeZoneNamesBundle { {"Africa/Porto-Novo", WAT}, {"Africa/Sao_Tome", GMT}, {"Africa/Timbuktu", GMT}, - {"Africa/Tripoli", EET}, + {"Africa/Tripoli", CET}, {"Africa/Tunis", CET}, {"Africa/Windhoek", WAT}, {"America/Adak", HAST}, @@ -646,6 +646,10 @@ public final class TimeZoneNames extends TimeZoneNamesBundle { {"Asia/Kashgar", CTT}, {"Asia/Kathmandu", NPT}, {"Asia/Katmandu", NPT}, + {"Asia/Khandyga", new String[] {"Khandyga Time", "YAKT", + "Khandyga Summer Time", "YAKST", + "Khandyga Time", "YAKT"}}, + {"Asia/Kolkata", IST}, {"Asia/Krasnoyarsk", new String[] {"Krasnoyarsk Time", "KRAT", "Krasnoyarsk Summer Time", "KRAST", @@ -703,6 +707,9 @@ public final class TimeZoneNames extends TimeZoneNamesBundle { {"Asia/Ulaanbaatar", ULAT}, {"Asia/Ulan_Bator", ULAT}, {"Asia/Urumqi", CTT}, + {"Asia/Ust-Nera", new String[] {"Ust-Nera Time", "VLAT", + "Ust-Nera Summer Time", "VLAST", + "Ust-Nera Time", "VLAT"}}, {"Asia/Vientiane", ICT}, {"Asia/Vladivostok", new String[] {"Vladivostok Time", "VLAT", "Vladivostok Summer Time", "VLAST", @@ -888,7 +895,7 @@ public final class TimeZoneNames extends TimeZoneNamesBundle { {"Jamaica", EST}, {"Japan", JST}, {"Kwajalein", MHT}, - {"Libya", EET}, + {"Libya", CET}, {"MET", new String[] {"Middle Europe Time", "MET", "Middle Europe Summer Time", "MEST", "Middle Europe Time", "MET"}}, diff --git a/jdk/src/share/classes/sun/util/resources/de/TimeZoneNames_de.java b/jdk/src/share/classes/sun/util/resources/de/TimeZoneNames_de.java index ab1e3673500..8dc71d7758f 100644 --- a/jdk/src/share/classes/sun/util/resources/de/TimeZoneNames_de.java +++ b/jdk/src/share/classes/sun/util/resources/de/TimeZoneNames_de.java @@ -289,7 +289,7 @@ public final class TimeZoneNames_de extends TimeZoneNamesBundle { {"Africa/Porto-Novo", WAT}, {"Africa/Sao_Tome", GMT}, {"Africa/Timbuktu", GMT}, - {"Africa/Tripoli", EET}, + {"Africa/Tripoli", CET}, {"Africa/Tunis", CET}, {"Africa/Windhoek", WAT}, {"America/Adak", HAST}, @@ -535,6 +535,8 @@ public final class TimeZoneNames_de extends TimeZoneNamesBundle { {"Asia/Kashgar", CTT}, {"Asia/Kathmandu", NPT}, {"Asia/Katmandu", NPT}, + {"Asia/Khandyga", new String[] {"Khandyga Time", "YAKT", + "Khandyga Summer Time", "YAKST"}}, {"Asia/Kolkata", IST}, {"Asia/Krasnoyarsk", new String[] {"Krasnojarsker Zeit", "KRAT", "Krasnojarsker Sommerzeit", "KRAST"}}, @@ -583,6 +585,8 @@ public final class TimeZoneNames_de extends TimeZoneNamesBundle { {"Asia/Ulaanbaatar", ULAT}, {"Asia/Ulan_Bator", ULAT}, {"Asia/Urumqi", CTT}, + {"Asia/Ust-Nera", new String[] {"Ust-Nera Time", "VLAT", + "Ust-Nera Summer Time", "VLAST"}}, {"Asia/Vientiane", ICT}, {"Asia/Vladivostok", new String[] {"Wladiwostok Zeit", "VLAT", "Wladiwostok Sommerzeit", "VLAST"}}, @@ -750,7 +754,7 @@ public final class TimeZoneNames_de extends TimeZoneNamesBundle { {"Jamaica", EST}, {"Japan", JST}, {"Kwajalein", MHT}, - {"Libya", EET}, + {"Libya", CET}, {"MET", new String[] {"Zentraleurop\u00e4ische Zeit", "MET", "Zentraleurop\u00e4ische Sommerzeit", "MEST"}}, {"Mexico/BajaNorte", PST}, diff --git a/jdk/src/share/classes/sun/util/resources/es/TimeZoneNames_es.java b/jdk/src/share/classes/sun/util/resources/es/TimeZoneNames_es.java index 7e4a41a4237..66e3e02acb2 100644 --- a/jdk/src/share/classes/sun/util/resources/es/TimeZoneNames_es.java +++ b/jdk/src/share/classes/sun/util/resources/es/TimeZoneNames_es.java @@ -289,7 +289,7 @@ public final class TimeZoneNames_es extends TimeZoneNamesBundle { {"Africa/Porto-Novo", WAT}, {"Africa/Sao_Tome", GMT}, {"Africa/Timbuktu", GMT}, - {"Africa/Tripoli", EET}, + {"Africa/Tripoli", CET}, {"Africa/Tunis", CET}, {"Africa/Windhoek", WAT}, {"America/Adak", HAST}, @@ -535,6 +535,8 @@ public final class TimeZoneNames_es extends TimeZoneNamesBundle { {"Asia/Kashgar", CTT}, {"Asia/Kathmandu", NPT}, {"Asia/Katmandu", NPT}, + {"Asia/Khandyga", new String[] {"Khandyga Time", "YAKT", + "Khandyga Summer Time", "YAKST"}}, {"Asia/Kolkata", IST}, {"Asia/Krasnoyarsk", new String[] {"Hora de Krasnoyarsk", "KRAT", "Hora de verano de Krasnoyarsk", "KRAST"}}, @@ -583,6 +585,8 @@ public final class TimeZoneNames_es extends TimeZoneNamesBundle { {"Asia/Ulaanbaatar", ULAT}, {"Asia/Ulan_Bator", ULAT}, {"Asia/Urumqi", CTT}, + {"Asia/Ust-Nera", new String[] {"Ust-Nera Time", "VLAT", + "Ust-Nera Summer Time", "VLAST"}}, {"Asia/Vientiane", ICT}, {"Asia/Vladivostok", new String[] {"Hora de Vladivostok", "VLAT", "Hora de verano de Vladivostok", "VLAST"}}, @@ -750,7 +754,7 @@ public final class TimeZoneNames_es extends TimeZoneNamesBundle { {"Jamaica", EST}, {"Japan", JST}, {"Kwajalein", MHT}, - {"Libya", EET}, + {"Libya", CET}, {"MET", new String[] {"Hora de Europa Central", "MET", "Hora de verano de Europa Central", "MEST"}}, {"Mexico/BajaNorte", PST}, diff --git a/jdk/src/share/classes/sun/util/resources/fr/TimeZoneNames_fr.java b/jdk/src/share/classes/sun/util/resources/fr/TimeZoneNames_fr.java index fc579bda857..2d960819cd7 100644 --- a/jdk/src/share/classes/sun/util/resources/fr/TimeZoneNames_fr.java +++ b/jdk/src/share/classes/sun/util/resources/fr/TimeZoneNames_fr.java @@ -289,7 +289,7 @@ public final class TimeZoneNames_fr extends TimeZoneNamesBundle { {"Africa/Porto-Novo", WAT}, {"Africa/Sao_Tome", GMT}, {"Africa/Timbuktu", GMT}, - {"Africa/Tripoli", EET}, + {"Africa/Tripoli", CET}, {"Africa/Tunis", CET}, {"Africa/Windhoek", WAT}, {"America/Adak", HAST}, @@ -535,6 +535,8 @@ public final class TimeZoneNames_fr extends TimeZoneNamesBundle { {"Asia/Kashgar", CTT}, {"Asia/Kathmandu", NPT}, {"Asia/Katmandu", NPT}, + {"Asia/Khandyga", new String[] {"Khandyga Time", "YAKT", + "Khandyga Summer Time", "YAKST"}}, {"Asia/Kolkata", IST}, {"Asia/Krasnoyarsk", new String[] {"Heure de Krasno\u00efarsk", "KRAT", "Heure d'\u00e9t\u00e9 de Krasno\u00efarsk", "KRAST"}}, @@ -583,6 +585,8 @@ public final class TimeZoneNames_fr extends TimeZoneNamesBundle { {"Asia/Ulaanbaatar", ULAT}, {"Asia/Ulan_Bator", ULAT}, {"Asia/Urumqi", CTT}, + {"Asia/Ust-Nera", new String[] {"Ust-Nera Time", "VLAT", + "Ust-Nera Summer Time", "VLAST"}}, {"Asia/Vientiane", ICT}, {"Asia/Vladivostok", new String[] {"Heure de Vladivostok", "VLAT", "Heure d'\u00e9t\u00e9 de Vladivostok", "VLAST"}}, @@ -750,7 +754,7 @@ public final class TimeZoneNames_fr extends TimeZoneNamesBundle { {"Jamaica", EST}, {"Japan", JST}, {"Kwajalein", MHT}, - {"Libya", EET}, + {"Libya", CET}, {"MET", new String[] {"Heure de l'Europe centrale", "MET", "Heure d'\u00e9t\u00e9 de l'Europe centrale", "MEST"}}, {"Mexico/BajaNorte", PST}, diff --git a/jdk/src/share/classes/sun/util/resources/it/TimeZoneNames_it.java b/jdk/src/share/classes/sun/util/resources/it/TimeZoneNames_it.java index 12cf3cfd7e9..18b434413b4 100644 --- a/jdk/src/share/classes/sun/util/resources/it/TimeZoneNames_it.java +++ b/jdk/src/share/classes/sun/util/resources/it/TimeZoneNames_it.java @@ -289,7 +289,7 @@ public final class TimeZoneNames_it extends TimeZoneNamesBundle { {"Africa/Porto-Novo", WAT}, {"Africa/Sao_Tome", GMT}, {"Africa/Timbuktu", GMT}, - {"Africa/Tripoli", EET}, + {"Africa/Tripoli", CET}, {"Africa/Tunis", CET}, {"Africa/Windhoek", WAT}, {"America/Adak", HAST}, @@ -536,6 +536,8 @@ public final class TimeZoneNames_it extends TimeZoneNamesBundle { {"Asia/Kathmandu", NPT}, {"Asia/Katmandu", NPT}, {"Asia/Kolkata", IST}, + {"Asia/Khandyga", new String[] {"Khandyga Time", "YAKT", + "Khandyga Summer Time", "YAKST"}}, {"Asia/Krasnoyarsk", new String[] {"Ora di Krasnojarsk", "KRAT", "Ora estiva di Krasnojarsk", "KRAST"}}, {"Asia/Kuala_Lumpur", MYT}, @@ -583,6 +585,8 @@ public final class TimeZoneNames_it extends TimeZoneNamesBundle { {"Asia/Ulaanbaatar", ULAT}, {"Asia/Ulan_Bator", ULAT}, {"Asia/Urumqi", CTT}, + {"Asia/Ust-Nera", new String[] {"Ust-Nera Time", "VLAT", + "Ust-Nera Summer Time", "VLAST"}}, {"Asia/Vientiane", ICT}, {"Asia/Vladivostok", new String[] {"Ora di Vladivostok", "VLAT", "Ora estiva di Vladivostok", "VLAST"}}, @@ -750,7 +754,7 @@ public final class TimeZoneNames_it extends TimeZoneNamesBundle { {"Jamaica", EST}, {"Japan", JST}, {"Kwajalein", MHT}, - {"Libya", EET}, + {"Libya", CET}, {"MET", new String[] {"Ora dell'Europa centrale", "MET", "Ora estiva dell'Europa centrale", "MEST"}}, {"Mexico/BajaNorte", PST}, diff --git a/jdk/src/share/classes/sun/util/resources/ja/TimeZoneNames_ja.java b/jdk/src/share/classes/sun/util/resources/ja/TimeZoneNames_ja.java index 0c1e64af729..5001fd28f04 100644 --- a/jdk/src/share/classes/sun/util/resources/ja/TimeZoneNames_ja.java +++ b/jdk/src/share/classes/sun/util/resources/ja/TimeZoneNames_ja.java @@ -289,7 +289,7 @@ public final class TimeZoneNames_ja extends TimeZoneNamesBundle { {"Africa/Porto-Novo", WAT}, {"Africa/Sao_Tome", GMT}, {"Africa/Timbuktu", GMT}, - {"Africa/Tripoli", EET}, + {"Africa/Tripoli", CET}, {"Africa/Tunis", CET}, {"Africa/Windhoek", WAT}, {"America/Adak", HAST}, @@ -535,6 +535,8 @@ public final class TimeZoneNames_ja extends TimeZoneNamesBundle { {"Asia/Kashgar", CTT}, {"Asia/Kathmandu", NPT}, {"Asia/Katmandu", NPT}, + {"Asia/Khandyga", new String[] {"Khandyga Time", "YAKT", + "Khandyga Summer Time", "YAKST"}}, {"Asia/Kolkata", IST}, {"Asia/Krasnoyarsk", new String[] {"\u30af\u30e9\u30b9\u30ce\u30e4\u30eb\u30b9\u30af\u6642\u9593", "KRAT", "\u30af\u30e9\u30b9\u30ce\u30e4\u30eb\u30b9\u30af\u590f\u6642\u9593", "KRAST"}}, @@ -583,6 +585,8 @@ public final class TimeZoneNames_ja extends TimeZoneNamesBundle { {"Asia/Ulaanbaatar", ULAT}, {"Asia/Ulan_Bator", ULAT}, {"Asia/Urumqi", CTT}, + {"Asia/Ust-Nera", new String[] {"Ust-Nera Time", "VLAT", + "Ust-Nera Summer Time", "VLAST"}}, {"Asia/Vientiane", ICT}, {"Asia/Vladivostok", new String[] {"\u30a6\u30e9\u30b8\u30aa\u30b9\u30c8\u30af\u6642\u9593", "VLAT", "\u30a6\u30e9\u30b8\u30aa\u30b9\u30c8\u30af\u590f\u6642\u9593", "VLAST"}}, @@ -750,7 +754,7 @@ public final class TimeZoneNames_ja extends TimeZoneNamesBundle { {"Jamaica", EST}, {"Japan", JST}, {"Kwajalein", MHT}, - {"Libya", EET}, + {"Libya", CET}, {"MET", new String[] {"\u4e2d\u90e8\u30e8\u30fc\u30ed\u30c3\u30d1\u6642\u9593", "MET", "\u4e2d\u90e8\u30e8\u30fc\u30ed\u30c3\u30d1\u590f\u6642\u9593", "MEST"}}, {"Mexico/BajaNorte", PST}, diff --git a/jdk/src/share/classes/sun/util/resources/ko/TimeZoneNames_ko.java b/jdk/src/share/classes/sun/util/resources/ko/TimeZoneNames_ko.java index f449bf3ab87..416a2c51214 100644 --- a/jdk/src/share/classes/sun/util/resources/ko/TimeZoneNames_ko.java +++ b/jdk/src/share/classes/sun/util/resources/ko/TimeZoneNames_ko.java @@ -289,7 +289,7 @@ public final class TimeZoneNames_ko extends TimeZoneNamesBundle { {"Africa/Porto-Novo", WAT}, {"Africa/Sao_Tome", GMT}, {"Africa/Timbuktu", GMT}, - {"Africa/Tripoli", EET}, + {"Africa/Tripoli", CET}, {"Africa/Tunis", CET}, {"Africa/Windhoek", WAT}, {"America/Adak", HAST}, @@ -535,6 +535,8 @@ public final class TimeZoneNames_ko extends TimeZoneNamesBundle { {"Asia/Kashgar", CTT}, {"Asia/Kathmandu", NPT}, {"Asia/Katmandu", NPT}, + {"Asia/Khandyga", new String[] {"Khandyga Time", "YAKT", + "Khandyga Summer Time", "YAKST"}}, {"Asia/Kolkata", IST}, {"Asia/Krasnoyarsk", new String[] {"\ud06c\ub77c\uc2a4\ub178\uc57c\ub974\uc2a4\ud06c \uc2dc\uac04", "KRAT", "\ud06c\ub77c\uc2a4\ub178\uc57c\ub974\uc2a4\ud06c \uc77c\uad11\uc808\uc57d\uc2dc\uac04", "KRAST"}}, @@ -583,6 +585,8 @@ public final class TimeZoneNames_ko extends TimeZoneNamesBundle { {"Asia/Ulaanbaatar", ULAT}, {"Asia/Ulan_Bator", ULAT}, {"Asia/Urumqi", CTT}, + {"Asia/Ust-Nera", new String[] {"Ust-Nera Time", "VLAT", + "Ust-Nera Summer Time", "VLAST" }}, {"Asia/Vientiane", ICT}, {"Asia/Vladivostok", new String[] {"\ube14\ub77c\ub514\ubcf4\uc2a4\ud1a1 \uc2dc\uac04", "VLAT", "\ube14\ub77c\ub514\ubcf4\uc2a4\ud1a1 \uc77c\uad11\uc808\uc57d\uc2dc\uac04", "VLAST"}}, @@ -750,7 +754,7 @@ public final class TimeZoneNames_ko extends TimeZoneNamesBundle { {"Jamaica", EST}, {"Japan", JST}, {"Kwajalein", MHT}, - {"Libya", EET}, + {"Libya", CET}, {"MET", new String[] {"\uc911\ubd80 \uc720\ub7fd \uc2dc\uac04", "MET", "\uc911\ubd80 \uc720\ub7fd \uc77c\uad11\uc808\uc57d\uc2dc\uac04", "MEST"}}, {"Mexico/BajaNorte", PST}, diff --git a/jdk/src/share/classes/sun/util/resources/pt/TimeZoneNames_pt_BR.java b/jdk/src/share/classes/sun/util/resources/pt/TimeZoneNames_pt_BR.java index 32d57832fea..124b8eb0c2d 100644 --- a/jdk/src/share/classes/sun/util/resources/pt/TimeZoneNames_pt_BR.java +++ b/jdk/src/share/classes/sun/util/resources/pt/TimeZoneNames_pt_BR.java @@ -289,7 +289,7 @@ public final class TimeZoneNames_pt_BR extends TimeZoneNamesBundle { {"Africa/Porto-Novo", WAT}, {"Africa/Sao_Tome", GMT}, {"Africa/Timbuktu", GMT}, - {"Africa/Tripoli", EET}, + {"Africa/Tripoli", CET}, {"Africa/Tunis", CET}, {"Africa/Windhoek", WAT}, {"America/Adak", HAST}, @@ -535,6 +535,8 @@ public final class TimeZoneNames_pt_BR extends TimeZoneNamesBundle { {"Asia/Kashgar", CTT}, {"Asia/Kathmandu", NPT}, {"Asia/Katmandu", NPT}, + {"Asia/Khandyga", new String[] {"Khandyga Time", "YAKT", + "Khandyga Summer Time", "YAKST"}}, {"Asia/Kolkata", IST}, {"Asia/Krasnoyarsk", new String[] {"Fuso hor\u00e1rio de Krasnoyarsk", "KRAT", "Fuso hor\u00e1rio de ver\u00e3o de Krasnoyarsk", "KRAST"}}, @@ -583,6 +585,8 @@ public final class TimeZoneNames_pt_BR extends TimeZoneNamesBundle { {"Asia/Ulaanbaatar", ULAT}, {"Asia/Ulan_Bator", ULAT}, {"Asia/Urumqi", CTT}, + {"Asia/Ust-Nera", new String[] {"Ust-Nera Time", "VLAT", + "Ust-Nera Summer Time", "VLAST"}}, {"Asia/Vientiane", ICT}, {"Asia/Vladivostok", new String[] {"Fuso hor\u00e1rio de Vladivostok", "VLAT", "Fuso hor\u00e1rio de ver\u00e3o de Vladivostok", "VLAST"}}, @@ -750,7 +754,7 @@ public final class TimeZoneNames_pt_BR extends TimeZoneNamesBundle { {"Jamaica", EST}, {"Japan", JST}, {"Kwajalein", MHT}, - {"Libya", EET}, + {"Libya", CET}, {"MET", new String[] {"Fuso hor\u00e1rio da Europa M\u00e9dia", "MET", "Fuso hor\u00e1rio de ver\u00e3o da Europa M\u00e9dia", "MEST"}}, {"Mexico/BajaNorte", PST}, diff --git a/jdk/src/share/classes/sun/util/resources/sv/TimeZoneNames_sv.java b/jdk/src/share/classes/sun/util/resources/sv/TimeZoneNames_sv.java index 65b1979888d..0e54cea9116 100644 --- a/jdk/src/share/classes/sun/util/resources/sv/TimeZoneNames_sv.java +++ b/jdk/src/share/classes/sun/util/resources/sv/TimeZoneNames_sv.java @@ -289,7 +289,7 @@ public final class TimeZoneNames_sv extends TimeZoneNamesBundle { {"Africa/Porto-Novo", WAT}, {"Africa/Sao_Tome", GMT}, {"Africa/Timbuktu", GMT}, - {"Africa/Tripoli", EET}, + {"Africa/Tripoli", CET}, {"Africa/Tunis", CET}, {"Africa/Windhoek", WAT}, {"America/Adak", HAST}, @@ -535,6 +535,8 @@ public final class TimeZoneNames_sv extends TimeZoneNamesBundle { {"Asia/Kashgar", CTT}, {"Asia/Kathmandu", NPT}, {"Asia/Katmandu", NPT}, + {"Asia/Khandyga", new String[] {"Khandyga Time", "YAKT", + "Khandyga Summer Time", "YAKST"}}, {"Asia/Kolkata", IST}, {"Asia/Krasnoyarsk", new String[] {"Krasnojarsk, normaltid", "KRAT", "Krasnojarsk, sommartid", "KRAST"}}, @@ -583,6 +585,8 @@ public final class TimeZoneNames_sv extends TimeZoneNamesBundle { {"Asia/Ulaanbaatar", ULAT}, {"Asia/Ulan_Bator", ULAT}, {"Asia/Urumqi", CTT}, + {"Asia/Ust-Nera", new String[] {"Ust-Nera Time", "VLAT", + "Ust-Nera Summer Time", "VLAST"}}, {"Asia/Vientiane", ICT}, {"Asia/Vladivostok", new String[] {"Vladivostok, normaltid", "VLAT", "Vladivostok, sommartid", "VLAST"}}, @@ -750,7 +754,7 @@ public final class TimeZoneNames_sv extends TimeZoneNamesBundle { {"Jamaica", EST}, {"Japan", JST}, {"Kwajalein", MHT}, - {"Libya", EET}, + {"Libya", CET}, {"MET", new String[] {"Mellaneuropeisk tid", "MET", "Mellaneuropeisk sommartid", "MEST"}}, {"Mexico/BajaNorte", PST}, diff --git a/jdk/src/share/classes/sun/util/resources/zh/TimeZoneNames_zh_CN.java b/jdk/src/share/classes/sun/util/resources/zh/TimeZoneNames_zh_CN.java index bd2333bdd30..74777e3619d 100644 --- a/jdk/src/share/classes/sun/util/resources/zh/TimeZoneNames_zh_CN.java +++ b/jdk/src/share/classes/sun/util/resources/zh/TimeZoneNames_zh_CN.java @@ -289,7 +289,7 @@ public final class TimeZoneNames_zh_CN extends TimeZoneNamesBundle { {"Africa/Porto-Novo", WAT}, {"Africa/Sao_Tome", GMT}, {"Africa/Timbuktu", GMT}, - {"Africa/Tripoli", EET}, + {"Africa/Tripoli", CET}, {"Africa/Tunis", CET}, {"Africa/Windhoek", WAT}, {"America/Adak", HAST}, @@ -535,6 +535,8 @@ public final class TimeZoneNames_zh_CN extends TimeZoneNamesBundle { {"Asia/Kashgar", CTT}, {"Asia/Kathmandu", NPT}, {"Asia/Katmandu", NPT}, + {"Asia/Khandyga", new String[] {"Khandyga Time", "YAKT", + "Khandyga Summer Time", "YAKST"}}, {"Asia/Kolkata", IST}, {"Asia/Krasnoyarsk", new String[] {"\u514b\u62c9\u65af\u8bfa\u4e9a\u5c14\u65af\u514b\u65f6\u95f4", "KRAT", "\u514b\u62c9\u65af\u8bfa\u4e9a\u5c14\u65af\u514b\u590f\u4ee4\u65f6", "KRAST"}}, @@ -583,6 +585,8 @@ public final class TimeZoneNames_zh_CN extends TimeZoneNamesBundle { {"Asia/Ulaanbaatar", ULAT}, {"Asia/Ulan_Bator", ULAT}, {"Asia/Urumqi", CTT}, + {"Asia/Ust-Nera", new String[] {"Ust-Nera Time", "VLAT", + "Ust-Nera Summer Time", "VLAST"}}, {"Asia/Vientiane", ICT}, {"Asia/Vladivostok", new String[] {"\u6d77\u53c2\u5d34\u65f6\u95f4", "VLAT", "\u6d77\u53c2\u5d34\u590f\u4ee4\u65f6", "VLAST"}}, @@ -750,7 +754,7 @@ public final class TimeZoneNames_zh_CN extends TimeZoneNamesBundle { {"Jamaica", EST}, {"Japan", JST}, {"Kwajalein", MHT}, - {"Libya", EET}, + {"Libya", CET}, {"MET", new String[] {"\u4e2d\u6b27\u65f6\u95f4", "MET", "\u4e2d\u6b27\u590f\u4ee4\u65f6", "MEST"}}, {"Mexico/BajaNorte", PST}, diff --git a/jdk/src/share/classes/sun/util/resources/zh/TimeZoneNames_zh_TW.java b/jdk/src/share/classes/sun/util/resources/zh/TimeZoneNames_zh_TW.java index 62b1a60e296..66de2c388af 100644 --- a/jdk/src/share/classes/sun/util/resources/zh/TimeZoneNames_zh_TW.java +++ b/jdk/src/share/classes/sun/util/resources/zh/TimeZoneNames_zh_TW.java @@ -289,7 +289,7 @@ public final class TimeZoneNames_zh_TW extends TimeZoneNamesBundle { {"Africa/Porto-Novo", WAT}, {"Africa/Sao_Tome", GMT}, {"Africa/Timbuktu", GMT}, - {"Africa/Tripoli", EET}, + {"Africa/Tripoli", CET}, {"Africa/Tunis", CET}, {"Africa/Windhoek", WAT}, {"America/Adak", HAST}, @@ -535,6 +535,8 @@ public final class TimeZoneNames_zh_TW extends TimeZoneNamesBundle { {"Asia/Kashgar", CTT}, {"Asia/Kathmandu", NPT}, {"Asia/Katmandu", NPT}, + {"Asia/Khandyga", new String[] {"Khandyga Time", "YAKT", + "Khandyga Summer Time", "YAKST"}}, {"Asia/Kolkata", IST}, {"Asia/Krasnoyarsk", new String[] {"\u514b\u62c9\u65af\u8afe\u4e9e\u723e\u65af\u514b\u6642\u9593", "KRAT", "\u514b\u62c9\u65af\u8afe\u4e9e\u723e\u65af\u514b\u590f\u4ee4\u6642\u9593", "KRAST"}}, @@ -584,6 +586,8 @@ public final class TimeZoneNames_zh_TW extends TimeZoneNamesBundle { {"Asia/Ulaanbaatar", ULAT}, {"Asia/Ulan_Bator", ULAT}, {"Asia/Urumqi", CTT}, + {"Asia/Ust-Nera", new String[] {"Ust-Nera Time", "VLAT", + "Ust-Nera Summer Time", "VLAST"}}, {"Asia/Vientiane", ICT}, {"Asia/Vladivostok", new String[] {"\u6d77\u53c3\u5d34\u6642\u9593", "VLAT", "\u6d77\u53c3\u5d34\u590f\u4ee4\u6642\u9593", "VLAST"}}, @@ -751,7 +755,7 @@ public final class TimeZoneNames_zh_TW extends TimeZoneNamesBundle { {"Jamaica", EST}, {"Japan", JST}, {"Kwajalein", MHT}, - {"Libya", EET}, + {"Libya", CET}, {"MET", new String[] {"\u4e2d\u6b50\u6642\u9593", "MET", "\u4e2d\u6b50\u590f\u4ee4\u6642\u9593", "MEST"}}, {"Mexico/BajaNorte", PST}, diff --git a/jdk/src/share/sample/scripting/scriptpad/src/com/sun/sample/scriptpad/Main.java b/jdk/src/share/sample/scripting/scriptpad/src/com/sun/sample/scriptpad/Main.java index e720687667e..aaeb58d0916 100644 --- a/jdk/src/share/sample/scripting/scriptpad/src/com/sun/sample/scriptpad/Main.java +++ b/jdk/src/share/sample/scripting/scriptpad/src/com/sun/sample/scriptpad/Main.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2006, 2011, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2006, 2013, Oracle and/or its affiliates. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -75,7 +75,7 @@ public class Main { */ InputStream is = Main.class.getResourceAsStream("/resources/" + name); // current script file name for better error messages - engine.put(ScriptEngine.NAME, name); + engine.put(ScriptEngine.FILENAME, name); // evaluate the script in the InputStream engine.eval(new InputStreamReader(is)); } diff --git a/jdk/src/share/sample/scripting/scriptpad/src/resources/Main.js b/jdk/src/share/sample/scripting/scriptpad/src/resources/Main.js index 9d1e5fe1d95..a1c332db310 100644 --- a/jdk/src/share/sample/scripting/scriptpad/src/resources/Main.js +++ b/jdk/src/share/sample/scripting/scriptpad/src/resources/Main.js @@ -1,5 +1,5 @@ /* - * Copyright (c) 2006, 2011, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2006, 2013, Oracle and/or its affiliates. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -37,7 +37,6 @@ * this sample code. */ - /* * This script can be loaded in jrunscript to start scriptpad. * @@ -48,4 +47,3 @@ load("conc.js"); load("gui.js"); load("scriptpad.js"); load("mm.js"); - diff --git a/jdk/src/share/sample/scripting/scriptpad/src/resources/conc.js b/jdk/src/share/sample/scripting/scriptpad/src/resources/conc.js index 1618b1d76d3..7e56fad4703 100644 --- a/jdk/src/share/sample/scripting/scriptpad/src/resources/conc.js +++ b/jdk/src/share/sample/scripting/scriptpad/src/resources/conc.js @@ -1,5 +1,5 @@ /* - * Copyright (c) 2006, 2011, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2006, 2013, Oracle and/or its affiliates. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -37,15 +37,43 @@ * this sample code. */ - /* * Concurrency utilities for JavaScript. These are based on - * java.lang and java.util.concurrent API. The following functions + * java.lang and java.util.concurrent API. The following functions * provide a simpler API for scripts. Instead of directly using java.lang * and java.util.concurrent classes, scripts can use functions and - * objects exported from here. + * objects exported from here. */ +// shortcut for j.u.c lock classes +var Lock = java.util.concurrent.locks.ReentrantLock; +var RWLock = java.util.concurrent.locks.ReentrantReadWriteLock; + +// check if there is a build in sync function, define one if missing +if (typeof sync === "undefined") { + var sync = function(func, obj) { + if (arguments.length < 1 || arguments.length > 2 ) { + throw "sync(function [,object]) parameter count mismatch"; + } + + var syncobj = (arguments.length == 2 ? obj : this); + + if (!syncobj._syncLock) { + syncobj._syncLock = new Lock(); + } + + return function() { + syncobj._syncLock.lock(); + try { + func.apply(null, arguments); + } finally { + syncobj._syncLock.unlock(); + } + }; + }; + sync.docString = "synchronize a function, optionally on an object"; +} + /** * Wrapper for java.lang.Object.wait * @@ -58,7 +86,6 @@ function wait(object) { } wait.docString = "convenient wrapper for java.lang.Object.wait method"; - /** * Wrapper for java.lang.Object.notify * @@ -71,7 +98,6 @@ function notify(object) { } notify.docString = "convenient wrapper for java.lang.Object.notify method"; - /** * Wrapper for java.lang.Object.notifyAll * @@ -84,7 +110,6 @@ function notifyAll(object) { } notifyAll.docString = "convenient wrapper for java.lang.Object.notifyAll method"; - /** * Creates a java.lang.Runnable from a given script * function. @@ -97,7 +122,7 @@ Function.prototype.runnable = function() { func.apply(null, args); } } -} +}; /** * Executes the function on a new Java Thread. @@ -106,7 +131,7 @@ Function.prototype.thread = function() { var t = new java.lang.Thread(this.runnable.apply(this, arguments)); t.start(); return t; -} +}; /** * Executes the function on a new Java daemon Thread. @@ -116,7 +141,7 @@ Function.prototype.daemon = function() { t.setDaemon(true); t.start(); return t; -} +}; /** * Creates a java.util.concurrent.Callable from a given script @@ -128,7 +153,7 @@ Function.prototype.callable = function() { return new java.util.concurrent.Callable() { call: function() { return func.apply(null, args); } } -} +}; /** * Registers the script function so that it will be called exit. @@ -137,10 +162,10 @@ Function.prototype.atexit = function () { var args = arguments; java.lang.Runtime.getRuntime().addShutdownHook( new java.lang.Thread(this.runnable.apply(this, args))); -} +}; /** - * Executes the function asynchronously. + * Executes the function asynchronously. * * @return a java.util.concurrent.FutureTask */ @@ -152,13 +177,9 @@ Function.prototype.future = (function() { (function() { theExecutor.shutdown(); }).atexit(); return function() { return theExecutor.submit(this.callable.apply(this, arguments)); - } + }; })(); -// shortcut for j.u.c lock classes -var Lock = java.util.concurrent.locks.ReentrantLock; -var RWLock = java.util.concurrent.locks.ReentrantReadWriteLock; - /** * Executes a function after acquiring given lock. On return, * (normal or exceptional), lock is released. @@ -179,7 +200,7 @@ Function.prototype.sync = function (lock) { } finally { lock.unlock(); } -} +}; /** * Causes current thread to sleep for specified @@ -193,30 +214,29 @@ function sleep(interval) { sleep.docString = "wrapper for java.lang.Thread.sleep method"; /** - * Schedules a task to be executed once in - * every N milliseconds specified. + * Schedules a task to be executed once in N milliseconds specified. * * @param callback function or expression to evaluate * @param interval in milliseconds to sleep * @return timeout ID (which is nothing but Thread instance) */ function setTimeout(callback, interval) { - if (! (callback instanceof Function)) { + if (! (callback instanceof Function) && typeof callback !== "function") { callback = new Function(callback); } // start a new thread that sleeps given time // and calls callback in an infinite loop return (function() { - while (true) { + try { sleep(interval); - callback(); - } + } catch (x) { } + callback(); }).daemon(); } -setTimeout.docString = "calls given callback once after specified interval" +setTimeout.docString = "calls given callback once after specified interval"; -/** +/** * Cancels a timeout set earlier. * @param tid timeout ID returned from setTimeout */ @@ -224,23 +244,62 @@ function clearTimeout(tid) { // we just interrupt the timer thread tid.interrupt(); } +clearTimeout.docString = "interrupt a setTimeout timer"; /** - * Simple access to thread local storage. + * Schedules a task to be executed once in + * every N milliseconds specified. + * + * @param callback function or expression to evaluate + * @param interval in milliseconds to sleep + * @return timeout ID (which is nothing but Thread instance) + */ +function setInterval(callback, interval) { + if (! (callback instanceof Function) && typeof callback !== "function") { + callback = new Function(callback); + } + + // start a new thread that sleeps given time + // and calls callback in an infinite loop + return (function() { + while (true) { + try { + sleep(interval); + } catch (x) { + break; + } + callback(); + } + }).daemon(); +} +setInterval.docString = "calls given callback every specified interval"; + +/** + * Cancels a timeout set earlier. + * @param tid timeout ID returned from setTimeout + */ +function clearInterval(tid) { + // we just interrupt the timer thread + tid.interrupt(); +} +clearInterval.docString = "interrupt a setInterval timer"; + +/** + * Simple access to thread local storage. * * Script sample: * * __thread.x = 44; - * function f() { - * __thread.x = 'hello'; - * print(__thread.x); + * function f() { + * __thread.x = 'hello'; + * print(__thread.x); * } * f.thread(); // prints 'hello' * print(__thread.x); // prints 44 in main thread */ var __thread = (function () { var map = new Object(); - return new JSAdapter() { + return new JSAdapter({ __has__: function(name) { return map[name] != undefined; }, @@ -263,8 +322,8 @@ var __thread = (function () { __delete__: function(name) { if (map[name] != undefined) { map[name].set(null); - } + } } - } + }); })(); diff --git a/jdk/src/share/sample/scripting/scriptpad/src/resources/gui.js b/jdk/src/share/sample/scripting/scriptpad/src/resources/gui.js index 950f64c0787..7882d2aa5c7 100644 --- a/jdk/src/share/sample/scripting/scriptpad/src/resources/gui.js +++ b/jdk/src/share/sample/scripting/scriptpad/src/resources/gui.js @@ -1,5 +1,5 @@ /* - * Copyright (c) 2006, 2011, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2006, 2013, Oracle and/or its affiliates. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -37,16 +37,15 @@ * this sample code. */ - /* - * Few user interface utilities. + * Few user interface utilities. */ if (this.window === undefined) { this.window = null; } -/** +/** * Swing invokeLater - invokes given function in AWT event thread */ Function.prototype.invokeLater = function() { @@ -54,13 +53,13 @@ Function.prototype.invokeLater = function() { var func = this; var args = arguments; SwingUtilities.invokeLater(new java.lang.Runnable() { - run: function() { + run: function() { func.apply(func, args); } }); -} +}; -/** +/** * Swing invokeAndWait - invokes given function in AWT event thread * and waits for it's completion */ @@ -69,11 +68,11 @@ Function.prototype.invokeAndWait = function() { var func = this; var args = arguments; SwingUtilities.invokeAndWait(new java.lang.Runnable() { - run: function() { + run: function() { func.apply(func, args); } }); -} +}; /** * Am I running in AWT event dispatcher thread? @@ -85,22 +84,24 @@ function isEventThread() { isEventThread.docString = "returns whether the current thread is GUI thread"; /** - * Opens a file dialog box + * Opens a file dialog box * * @param curDir current directory [optional] * @param save flag tells whether this is a save dialog or not * @return selected file or else null */ -function fileDialog(curDir, save) { +function fileDialog(curDir, save) { var result; function _fileDialog() { if (curDir == undefined) { curDir = new java.io.File("."); } + var JFileChooser = javax.swing.JFileChooser; - var dialog = new JFileChooser(curDir); - var res = save? dialog.showSaveDialog(window): - dialog.showOpenDialog(window); + var dialog = new JFileChooser(curDir); + var res = save ? dialog.showSaveDialog(window): + dialog.showOpenDialog(window); + if (res == JFileChooser.APPROVE_OPTION) { result = dialog.getSelectedFile(); } else { @@ -113,37 +114,41 @@ function fileDialog(curDir, save) { } else { _fileDialog.invokeAndWait(); } + return result; } fileDialog.docString = "show a file dialog box"; /** - * Opens a color chooser dialog box + * Opens a color chooser dialog box * * @param title of the dialog box [optional] * @param color default color [optional] * @return choosen color or default color */ - function colorDialog(title, color) { var result; + function _colorDialog() { if (title == undefined) { title = "Choose Color"; } + if (color == undefined) { color = java.awt.Color.BLACK; } + var chooser = new javax.swing.JColorChooser(); var res = chooser.showDialog(window, title, color); - result = res? res : color; - } + result = res ? res : color; + } if (isEventThread()) { _colorDialog(); } else { _colorDialog.invokeAndWait(); } + return result; } colorDialog.docString = "shows a color chooser dialog box"; @@ -156,15 +161,15 @@ colorDialog.docString = "shows a color chooser dialog box"; * @param msgType type of message box [constants in JOptionPane] */ function msgBox(msg, title, msgType) { - - function _msgBox() { + function _msgBox() { var JOptionPane = javax.swing.JOptionPane; if (msg === undefined) msg = "undefined"; if (msg === null) msg = "null"; if (title == undefined) title = msg; - if (msgType == undefined) type = JOptionPane.INFORMATION_MESSAGE; + if (msgType == undefined) msgType = JOptionPane.INFORMATION_MESSAGE; JOptionPane.showMessageDialog(window, msg, title, msgType); } + if (isEventThread()) { _msgBox(); } else { @@ -172,13 +177,13 @@ function msgBox(msg, title, msgType) { } } msgBox.docString = "shows MessageBox to the user"; - + /** * Shows an information alert box * * @param msg message to be shown * @param title title of message box [optional] - */ + */ function alert(msg, title) { var JOptionPane = javax.swing.JOptionPane; msgBox(msg, title, JOptionPane.INFORMATION_MESSAGE); @@ -197,7 +202,6 @@ function error(msg, title) { } error.docString = "shows an error message box to the user"; - /** * Shows a warning alert box * @@ -210,7 +214,6 @@ function warn(msg, title) { } warn.docString = "shows a warning message box to the user"; - /** * Shows a prompt dialog box * @@ -225,11 +228,13 @@ function prompt(question, answer) { if (answer == undefined) answer = ""; result = JOptionPane.showInputDialog(window, question, answer); } + if (isEventThread()) { _prompt(); } else { _prompt.invokeAndWait(); } + return result; } prompt.docString = "shows a prompt box to the user and returns the answer"; @@ -244,30 +249,33 @@ prompt.docString = "shows a prompt box to the user and returns the answer"; function confirm(msg, title) { var result; var JOptionPane = javax.swing.JOptionPane; + function _confirm() { if (title == undefined) title = msg; var optionType = JOptionPane.YES_NO_OPTION; result = JOptionPane.showConfirmDialog(window, msg, title, optionType); } + if (isEventThread()) { _confirm(); } else { _confirm.invokeAndWait(); - } + } + return result == JOptionPane.YES_OPTION; } confirm.docString = "shows a confirmation message box to the user"; /** - * Exit the process after confirmation from user - * + * Exit the process after confirmation from user + * * @param exitCode return code to OS [optional] */ function exit(exitCode) { if (exitCode == undefined) exitCode = 0; if (confirm("Do you really want to exit?")) { java.lang.System.exit(exitCode); - } + } } exit.docString = "exits jconsole"; diff --git a/jdk/src/share/sample/scripting/scriptpad/src/resources/mm.js b/jdk/src/share/sample/scripting/scriptpad/src/resources/mm.js index 1e6d2305c76..2b75f243bfc 100644 --- a/jdk/src/share/sample/scripting/scriptpad/src/resources/mm.js +++ b/jdk/src/share/sample/scripting/scriptpad/src/resources/mm.js @@ -1,5 +1,5 @@ /* - * Copyright (c) 2006, 2011, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2006, 2013, Oracle and/or its affiliates. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -37,10 +37,9 @@ * this sample code. */ - /* * This is a collection of utilities for Monitoring - * and management API. + * and management API. * * File dependency: * conc.js -> for concurrency utilities @@ -71,14 +70,14 @@ function jmxConnect(hostport) { } jmxConnect.docString = "connects to the given host, port (specified as name:port)"; -function mbeanConnection() { - if (mmConnection == null) { +function mbeanConnection() { + if (mmConnection == null) { throw "Not connected to MBeanServer yet!"; } return mmConnection; } -mbeanConnection.docString = "returns the current MBeanServer connection" +mbeanConnection.docString = "returns the current MBeanServer connection"; /** * Returns a platform MXBean proxy for given MXBean name and interface class @@ -102,7 +101,6 @@ function objectName(objName) { } objectName.docString = "creates JMX ObjectName for a given String"; - /** * Creates a new (M&M) Attribute object * @@ -146,7 +144,6 @@ function queryNames(objName, query) { } queryNames.docString = "returns QueryNames using given ObjectName and optional query"; - /** * Queries with given ObjectName and QueryExp. * QueryExp may be null. @@ -220,7 +217,6 @@ function getMBeanAttribute(objName, attrName) { } getMBeanAttribute.docString = "returns a single Attribute of given ObjectName"; - // sets MBean attributes function setMBeanAttributes(objName, attrList) { objName = objectName(objName); @@ -237,7 +233,6 @@ function setMBeanAttribute(objName, attrName, attrValue) { } setMBeanAttribute.docString = "sets a single Attribute of given ObjectName"; - // invokes an operation on given MBean function invokeMBean(objName, operation, params, signature) { objName = objectName(objName); @@ -260,16 +255,17 @@ invokeMBean.docString = "invokes MBean operation on given ObjectName"; * will be of type FutureTask. When you need value, call 'get' on it. */ function mbean(objName, async) { + var index; objName = objectName(objName); - var info = mbeanInfo(objName); + var info = mbeanInfo(objName); var attrs = info.attributes; var attrMap = new Object; - for (var index in attrs) { + for (index in attrs) { attrMap[attrs[index].name] = attrs[index]; } var opers = info.operations; var operMap = new Object; - for (var index in opers) { + for (index in opers) { operMap[opers[index].name] = opers[index]; } @@ -288,9 +284,9 @@ function mbean(objName, async) { __get__: function (name) { if (isAttribute(name)) { if (async) { - return getMBeanAttribute.future(objName, name); + return getMBeanAttribute.future(objName, name); } else { - return getMBeanAttribute(objName, name); + return getMBeanAttribute(objName, name); } } else if (isOperation(name)) { var oper = operMap[name]; @@ -302,12 +298,12 @@ function mbean(objName, async) { sigNames[index] = sigs[index].getType(); } if (async) { - return invokeMBean.future(objName, name, + return invokeMBean.future(objName, name, params, sigNames); } else { return invokeMBean(objName, name, params, sigNames); } - } + }; } else { return undefined; } @@ -327,9 +323,9 @@ function mbean(objName, async) { } mbean.docString = "returns a conveninent script wrapper for a MBean of given ObjectName"; -if (this.application != undefined) { - this.application.addTool("JMX Connect", - // connect to a JMX MBean Server +if (this.application != undefined) { + this.application.addTool("JMX Connect", + // connect to a JMX MBean Server function () { var url = prompt("Connect to JMX server (host:port)"); if (url != null) { diff --git a/jdk/src/share/sample/scripting/scriptpad/src/resources/scriptpad.js b/jdk/src/share/sample/scripting/scriptpad/src/resources/scriptpad.js index 43507043d82..807d05e1ba2 100644 --- a/jdk/src/share/sample/scripting/scriptpad/src/resources/scriptpad.js +++ b/jdk/src/share/sample/scripting/scriptpad/src/resources/scriptpad.js @@ -1,5 +1,5 @@ /* - * Copyright (c) 2006, 2011, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2006, 2013, Oracle and/or its affiliates. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -37,9 +37,8 @@ * this sample code. */ - /* - * This script creates a simple Notepad-like interface, which + * This script creates a simple Notepad-like interface, which * serves as a simple script editor, runner. * * File dependency: @@ -47,6 +46,11 @@ * gui.js -> for basic GUI functions */ +/* + * globalThis is used for actionHelpGlobals() and showFrame(). + */ +var globalThis = this; + /* * JavaImporter helps in avoiding pollution of JavaScript * global namespace. We can import multiple Java packages @@ -54,573 +58,572 @@ * statement. */ var guiPkgs = new JavaImporter(java.awt, java.awt.event, - javax.swing, javax.swing.undo, - javax.swing.event, javax.swing.text); + javax.swing, javax.swing.undo, + javax.swing.event, javax.swing.text); -with (guiPkgs) { - - /* - * within this "with" statement all Java classes in - * packages defined in "guiPkgs" can be used as simple - * names instead of the fully qualified names. - */ - - - // main entry point of the scriptpad application - function main() { - function createEditor() { - var c = new JTextArea(); - c.setDragEnabled(true); - c.setFont(new Font("monospaced", Font.PLAIN, 12)); - return c; - } - - /*const*/ var titleSuffix = "- Scriptpad"; - /*const*/ var defaultTitle = "Untitled" + titleSuffix; - - // Scriptpad's main frame - var frame; - // Scriptpad's main editor - var editor; - - // To track the current file name - var curFileName = null; - - // To track whether the current document - // has been modified or not - var docChanged = false; - - // check and alert user for unsaved - // but modified document - function checkDocChanged() { - if (docChanged) { - // ignore zero-content untitled document - if (curFileName == null && - editor.document.length == 0) { - return; - } - - if (confirm( - "Do you want to save the changes?", - "The document has changed")) { - actionSave(); - } - } - } - - // set a document listener to track - // whether that is modified or not - function setDocListener() { - var doc = editor.getDocument(); - docChanged = false; - doc.addDocumentListener(new DocumentListener() { - equals: function(o) { return this === o; }, - toString: function() { return "doc listener"; }, - changeUpdate: function() { docChanged = true; }, - insertUpdate: function() { docChanged = true; }, - removeUpdate: function() { docChanged = true; }, - }); - } - - // menu action functions - - // "File" menu - - // create a "new" document - function actionNew(){ - checkDocChanged(); - curFileName = null; - editor.setDocument(new PlainDocument()); - setDocListener(); - frame.setTitle(defaultTitle); - editor.revalidate(); - } - - // open an existing file - function actionOpen() { - checkDocChanged(); - var f = fileDialog(); - if (f == null) { - return; - } - if (f.isFile() && f.canRead()) { - frame.setTitle(f.getName() + titleSuffix); - editor.setDocument(new PlainDocument()); - var progress = new JProgressBar(); - progress.setMinimum(0); - progress.setMaximum(f.length()); - var doc = editor.getDocument(); - var inp = new java.io.FileReader(f); - var buff = java.lang.reflect.Array.newInstance( - java.lang.Character.TYPE, 4096); - var nch; - while ((nch = inp.read(buff, 0, buff.length)) != -1) { - doc.insertString(doc.getLength(), - new java.lang.String(buff, 0, nch), null); - progress.setValue(progress.getValue() + nch); - } - inp.close(); - curFileName = f.getAbsolutePath(); - setDocListener(); - } else { - error("Can not open file: " + f, - "Error opening file: " + f); - } - } - - // open script from a URL - function actionOpenURL() { - checkDocChanged(); - var url = prompt("Address:"); - if (url == null) { - return; - } - - try { - var u = new java.net.URL(url); - editor.setDocument(new PlainDocument()); - frame.setTitle(url + titleSuffix); - var progress = new JProgressBar(); - progress.setMinimum(0); - progress.setIndeterminate(true); - var doc = editor.getDocument(); - var inp = new java.io.InputStreamReader(u.openStream()); - var buff = java.lang.reflect.Array.newInstance( - java.lang.Character.TYPE, 4096); - var nch; - while ((nch = inp.read(buff, 0, buff.length)) != -1) { - doc.insertString(doc.getLength(), - new java.lang.String(buff, 0, nch), null); - progress.setValue(progress.getValue() + nch); - } - curFileName = null; - setDocListener(); - } catch (e) { - error("Error opening URL: " + e, - "Can not open URL: " + url); - } - } - - // factored out "save" function used by - // save, save as menu actions - function save(file) { - var doc = editor.getDocument(); - frame.setTitle(file.getName() + titleSuffix); - curFileName = file; - var progress = new JProgressBar(); - progress.setMinimum(0); - progress.setMaximum(file.length()); - var out = new java.io.FileWriter(file); - var text = new Segment(); - text.setPartialReturn(true); - var charsLeft = doc.getLength(); - var offset = 0; - while (charsLeft > 0) { - doc.getText(offset, java.lang.Math.min(4096, charsLeft), text); - out.write(text.array, text.offset, text.count); - charsLeft -= text.count; - offset += text.count; - progress.setValue(offset); - java.lang.Thread.sleep(10); - } - out.flush(); - out.close(); - docChanged = false; - } - - // file-save as menu - function actionSaveAs() { - var ret = fileDialog(null, true); - if (ret == null) { - return; - } - save(ret); - } - - // file-save menu - function actionSave() { - if (curFileName) { - save(new java.io.File(curFileName)); - } else { - actionSaveAs(); - } - } - - // exit from scriptpad - function actionExit() { - checkDocChanged(); - java.lang.System.exit(0); - } - - // "Edit" menu - - // cut the currently selected text - function actionCut() { - editor.cut(); - } - - // copy the currently selected text to clipboard - function actionCopy() { - editor.copy(); - } - - // paste clipboard content to document - function actionPaste() { - editor.paste(); - } - - // select all the text in editor - function actionSelectAll() { - editor.selectAll(); - } - - // "Tools" menu - - // run the current document as JavaScript - function actionRun() { - var doc = editor.getDocument(); - var script = doc.getText(0, doc.getLength()); - var oldFile = engine.get(javax.script.ScriptEngine.FILENAME); - try { - if (this.engine == undefined) { - var m = new javax.script.ScriptEngineManager(); - engine = m.getEngineByName("js"); - } - engine.put(javax.script.ScriptEngine.FILENAME, frame.title); - engine.eval(script, context); - } catch (e) { - error(e, "Script Error"); - // e.rhinoException.printStackTrace(); - } finally { - engine.put(javax.script.ScriptEngine.FILENAME, oldFile); - } - } - - // "Examples" menu - - // show given script as new document - function showScript(title, str) { - actionNew(); - frame.setTitle("Example - " + title + titleSuffix); - var doc = editor.document; - doc.insertString(0, str, null); - } - - // "hello world" - function actionHello() { - showScript(arguments.callee.title, - "alert('Hello, world');"); - } - actionHello.title = "Hello, World"; - - // eval the "hello world"! - function actionEval() { - showScript(actionEval.title, - "eval(\"alert('Hello, world')\");"); - } - actionEval.title = "Eval"; - - // show how to access Java static methods - function actionJavaStatic() { - showScript(arguments.callee.title, - "// Just use Java syntax\n" + - "var props = java.lang.System.getProperties();\n" + - "alert(props.get('os.name'));"); - } - actionJavaStatic.title = "Java Static Calls"; - - // show how to access Java classes, methods - function actionJavaAccess() { - showScript(arguments.callee.title, - "// just use new JavaClass();\n" + - "var fr = new javax.swing.JFrame();\n" + - "// call all public methods as in Java\n" + - "fr.setTitle('hello');\n" + - "fr.setDefaultCloseOperation(javax.swing.WindowConstants.DISPOSE_ON_CLOSE);\n" + - "fr.setSize(200, 200);\n" + - "fr.setVisible(true);"); - } - actionJavaAccess.title = "Java Object Access"; - - // show how to use Java bean conventions - function actionJavaBean() { - showScript(arguments.callee.title, - "var fr = new javax.swing.JFrame();\n" + - "fr.setSize(200, 200);\n" + - "// access public get/set methods as fields\n" + - "fr.defaultCloseOperation = javax.swing.WindowConstants.DISPOSE_ON_CLOSE;\n" + - "fr.title = 'hello';\n" + - "fr.visible = true;"); - } - actionJavaBean.title = "Java Beans"; - - // show how to implement Java interface - function actionJavaInterface() { - showScript(arguments.callee.title, - "// use Java anonymizer class-like syntax!\n" + - "var r = new java.lang.Runnable() {\n" + - " run: function() {\n" + - " alert('hello');\n" + - " }\n" + - " };\n" + - "// use the above Runnable to create a Thread\n" + - "java.lang.Thread(r).start();\n" + - "// For simple one method interfaces, just pass script function\n" + - "java.lang.Thread(function() { alert('world'); }).start();"); - } - actionJavaInterface.title = "Java Interfaces"; - - // show how to import Java classes, packages - function actionJavaImport() { - showScript(arguments.callee.title, - "// use Java-like import *...\n" + - "// importPackage(java.io);\n" + - "// or import a specific class\n" + - "// importClass(java.io.File);\n" + - "// or better - import just within a scope!\n" + - "var ioPkgs = JavaImporter(java.io);\n" + - "with (ioPkgs) { alert(new File('.').absolutePath); }"); - } - actionJavaImport.title = "Java Import"; - - // "Help" menu - - /* - * Shows a one liner help message for each - * global function. Note that this function - * depends on docString meta-data for each - * function. - */ - function actionHelpGlobals() { - var names = new java.util.ArrayList(); - for (var i in this) { - var func = this[i]; - if (typeof(func) == "function" && - ("docString" in func)) { - names.add(i); - } - } - java.util.Collections.sort(names); - var helpDoc = new java.lang.StringBuffer(); - helpDoc.append(""); - var itr = names.iterator(); - while (itr.hasNext()) { - var name = itr.next(); - helpDoc.append(""); - } - helpDoc.append("
      "); - helpDoc.append(name); - helpDoc.append(""); - helpDoc.append(this[name].docString); - helpDoc.append("
      "); - - var helpEditor = new JEditorPane(); - helpEditor.setContentType("text/html"); - helpEditor.setEditable(false); - helpEditor.setText(helpDoc.toString()); - - var scroller = new JScrollPane(); - var port = scroller.getViewport(); - port.add(helpEditor); - - var helpFrame = new JFrame("Help - Global Functions"); - helpFrame.getContentPane().add("Center", scroller); - helpFrame.setDefaultCloseOperation(WindowConstants.DISPOSE_ON_CLOSE); - helpFrame.pack(); - helpFrame.setSize(500, 600); - helpFrame.setVisible(true); - } - - // show a simple about message for scriptpad - function actionAbout() { - alert("Scriptpad\nVersion 1.0", "Scriptpad"); - } - - /* - * This data is used to construct menu bar. - * This way adding a menu is easier. Just add - * top level menu or add an item to an existing - * menu. "action" should be a function that is - * called back on clicking the correponding menu. - */ - var menuData = [ - { - menu: "File", - items: [ - { name: "New", action: actionNew , accel: KeyEvent.VK_N }, - { name: "Open...", action: actionOpen, accel: KeyEvent.VK_O }, - { name: "Open URL...", action: actionOpenURL, accel: KeyEvent.VK_U }, - { name: "Save", action: actionSave, accel: KeyEvent.VK_S }, - { name: "Save As...", action: actionSaveAs }, - { name: "-" }, - { name: "Exit", action: actionExit } - ] - }, - - { - menu: "Edit", - items: [ - { name: "Cut", action: actionCut, accel: KeyEvent.VK_X }, - { name: "Copy", action: actionCopy, accel: KeyEvent.VK_C }, - { name: "Paste", action: actionPaste, accel: KeyEvent.VK_V }, - { name: "-" }, - { name: "Select All", action: actionSelectAll, accel: KeyEvent.VK_A } - ] - }, - - { - menu: "Tools", - items: [ - { name: "Run", action: actionRun, accel: KeyEvent.VK_R }, - ] - }, - - { - menu: "Examples", - items: [ - { name: actionHello.title, action: actionHello }, - { name: actionEval.title, action: actionEval }, - { name: actionJavaStatic.title, action: actionJavaStatic }, - { name: actionJavaAccess.title, action: actionJavaAccess }, - { name: actionJavaBean.title, action: actionJavaBean }, - { name: actionJavaInterface.title, action: actionJavaInterface }, - { name: actionJavaImport.title, action: actionJavaImport }, - ] - }, - - { - menu: "Help", - items: [ - { name: "Global Functions", action: actionHelpGlobals }, - { name: "-" }, - { name: "About Scriptpad", action: actionAbout }, - ] - } - ]; - - - function setMenuAccelerator(mi, accel) { - var keyStroke = KeyStroke.getKeyStroke(accel, - Toolkit.getDefaultToolkit().getMenuShortcutKeyMask(), false); - mi.setAccelerator(keyStroke); - } - - // create a menubar using the above menu data - function createMenubar() { - var mb = new JMenuBar(); - for (var m in menuData) { - var items = menuData[m].items; - var menu = new JMenu(menuData[m].menu); - for (var i in items) { - if (items[i].name.equals("-")) { - menu.addSeparator(); - } else { - var mi = new JMenuItem(items[i].name); - var action = items[i].action; - mi.addActionListener(action); - var accel = items[i].accel; - if (accel) { - setMenuAccelerator(mi, accel); - } - menu.add(mi); - } - } - mb.add(menu); - } - return mb; - } - - // function to add a new menu item under "Tools" menu - function addTool(menuItem, action, accel) { - if (typeof(action) != "function") { - return; - } - - var toolsIndex = -1; - // find the index of the "Tools" menu - for (var i in menuData) { - if (menuData[i].menu.equals("Tools")) { - toolsIndex = i; - break; - } - } - if (toolsIndex == -1) { - return; - } - var toolsMenu = frame.getJMenuBar().getMenu(toolsIndex); - var mi = new JMenuItem(menuItem); - mi.addActionListener(action); - if (accel) { - setMenuAccelerator(mi, accel); - } - toolsMenu.add(mi); - } - - - // create Scriptpad frame - function createFrame() { - frame = new JFrame(); - frame.setTitle(defaultTitle); - frame.setBackground(Color.lightGray); - frame.getContentPane().setLayout(new BorderLayout()); - - // create notepad panel - var notepad = new JPanel(); - notepad.setBorder(BorderFactory.createEtchedBorder()); - notepad.setLayout(new BorderLayout()); - - // create editor - editor = createEditor(); - var scroller = new JScrollPane(); - var port = scroller.getViewport(); - port.add(editor); - - // add editor to notepad panel - var panel = new JPanel(); - panel.setLayout(new BorderLayout()); - panel.add("Center", scroller); - notepad.add("Center", panel); - - // add notepad panel to frame - frame.getContentPane().add("Center", notepad); - - // set menu bar to frame and show the frame - frame.setJMenuBar(createMenubar()); - frame.setDefaultCloseOperation(JFrame.EXIT_ON_CLOSE); - frame.pack(); - frame.setSize(500, 600); - } - - // show Scriptpad frame - function showFrame() { - // set global variable by the name "window" - this.window = frame; - - // open new document - actionNew(); - - frame.setVisible(true); - } - - // create and show Scriptpad frame - createFrame(); - showFrame(); - - /* - * Application object has two fields "frame", "editor" - * which are current JFrame and editor and a method - * called "addTool" to add new menu item to "Tools" menu. - */ - return { - frame: frame, - editor: editor, - addTool: addTool - }; +// main entry point of the scriptpad application +var main = function() { + function createEditor() { + var c = new guiPkgs.JTextArea(); + c.setDragEnabled(true); + c.setFont(new guiPkgs.Font("monospaced", guiPkgs.Font.PLAIN, 12)); + return c; } -} + + /*const*/ var titleSuffix = "- Scriptpad"; + /*const*/ var defaultTitle = "Untitled" + titleSuffix; + + // Scriptpad's main frame + var frame; + // Scriptpad's main editor + var editor; + + // To track the current file name + var curFileName = null; + + // To track whether the current document + // has been modified or not + var docChanged = false; + + // check and alert user for unsaved + // but modified document + function checkDocChanged() { + if (docChanged) { + // ignore zero-content untitled document + if (curFileName == null && + editor.document.length == 0) { + return; + } + + if (confirm("Do you want to save the changes?", + "The document has changed")) { + actionSave(); + } + } + } + + // set a document listener to track + // whether that is modified or not + function setDocListener() { + var doc = editor.getDocument(); + docChanged = false; + doc.addDocumentListener( new guiPkgs.DocumentListener() { + equals: function(o) { + return this === o; }, + toString: function() { + return "doc listener"; }, + changeUpdate: function() { + docChanged = true; }, + insertUpdate: function() { + docChanged = true; }, + removeUpdate: function() { + docChanged = true; } + }); + } + + // menu action functions + + // "File" menu + + // create a "new" document + function actionNew() { + checkDocChanged(); + curFileName = null; + editor.setDocument(new guiPkgs.PlainDocument()); + setDocListener(); + frame.setTitle(defaultTitle); + editor.revalidate(); + } + + // open an existing file + function actionOpen() { + checkDocChanged(); + var f = fileDialog(); + if (f == null) { + return; + } + + if (f.isFile() && f.canRead()) { + frame.setTitle(f.getName() + titleSuffix); + editor.setDocument(new guiPkgs.PlainDocument()); + var progress = new guiPkgs.JProgressBar(); + progress.setMinimum(0); + progress.setMaximum(f.length()); + var doc = editor.getDocument(); + var inp = new java.io.FileReader(f); + var buff = java.lang.reflect.Array.newInstance( + java.lang.Character.TYPE, 4096); + var nch; + while ((nch = inp.read(buff, 0, buff.length)) != -1) { + doc.insertString(doc.getLength(), + new java.lang.String(buff, 0, nch), null); + progress.setValue(progress.getValue() + nch); + } + inp.close(); + curFileName = f.getAbsolutePath(); + setDocListener(); + } else { + error("Can not open file: " + f, + "Error opening file: " + f); + } + } + + // open script from a URL + function actionOpenURL() { + checkDocChanged(); + var url = prompt("Address:"); + if (url == null) { + return; + } + + try { + var u = new java.net.URL(url); + editor.setDocument(new guiPkgs.PlainDocument()); + frame.setTitle(url + titleSuffix); + var progress = new guiPkgs.JProgressBar(); + progress.setMinimum(0); + progress.setIndeterminate(true); + var doc = editor.getDocument(); + var inp = new java.io.InputStreamReader(u.openStream()); + var buff = java.lang.reflect.Array.newInstance( + java.lang.Character.TYPE, 4096); + var nch; + while ((nch = inp.read(buff, 0, buff.length)) != -1) { + doc.insertString(doc.getLength(), + new java.lang.String(buff, 0, nch), null); + progress.setValue(progress.getValue() + nch); + } + curFileName = null; + setDocListener(); + } catch (e) { + error("Error opening URL: " + e, + "Can not open URL: " + url); + } + } + + // factored out "save" function used by + // save, save as menu actions + function save(file) { + var doc = editor.getDocument(); + frame.setTitle(file.getName() + titleSuffix); + curFileName = file; + var progress = new guiPkgs.JProgressBar(); + progress.setMinimum(0); + progress.setMaximum(file.length()); + var out = new java.io.FileWriter(file); + var text = new guiPkgs.Segment(); + text.setPartialReturn(true); + var charsLeft = doc.getLength(); + var offset = 0; + var min; + + while (charsLeft > 0) { + doc.getText(offset, Math.min(4096, charsLeft), text); + out.write(text.array, text.offset, text.count); + charsLeft -= text.count; + offset += text.count; + progress.setValue(offset); + java.lang.Thread.sleep(10); + } + + out.flush(); + out.close(); + docChanged = false; + } + + // file-save as menu + function actionSaveAs() { + var ret = fileDialog(null, true); + if (ret == null) { + return; + } + save(ret); + } + + // file-save menu + function actionSave() { + if (curFileName) { + save(new java.io.File(curFileName)); + } else { + actionSaveAs(); + } + } + + // exit from scriptpad + function actionExit() { + checkDocChanged(); + java.lang.System.exit(0); + } + + // "Edit" menu + + // cut the currently selected text + function actionCut() { + editor.cut(); + } + + // copy the currently selected text to clipboard + function actionCopy() { + editor.copy(); + } + + // paste clipboard content to document + function actionPaste() { + editor.paste(); + } + + // select all the text in editor + function actionSelectAll() { + editor.selectAll(); + } + + // "Tools" menu + + // run the current document as JavaScript + function actionRun() { + var doc = editor.getDocument(); + var script = doc.getText(0, doc.getLength()); + var oldFile = engine.get(javax.script.ScriptEngine.FILENAME); + try { + if (engine == undefined) { + var m = new javax.script.ScriptEngineManager(); + engine = m.getEngineByName("nashorn"); + } + engine.put(javax.script.ScriptEngine.FILENAME, frame.title); + engine.eval(script, context); + } catch (e) { + error(e, "Script Error"); + e.printStackTrace(); + } finally { + engine.put(javax.script.ScriptEngine.FILENAME, oldFile); + } + } + + // "Examples" menu + + // show given script as new document + function showScript(title, str) { + actionNew(); + frame.setTitle("Example - " + title + titleSuffix); + var doc = editor.document; + doc.insertString(0, str, null); + } + + // "hello world" + function actionHello() { + showScript(actionEval.title, + "alert('Hello, world');"); + } + actionHello.title = "Hello, World"; + + // eval the "hello world"! + function actionEval() { + showScript(actionEval.title, + "eval(\"alert('Hello, world')\");"); + } + actionEval.title = "Eval"; + + // show how to access Java static methods + function actionJavaStatic() { + showScript(arguments.callee.title, + "// Just use Java syntax\n" + + "var props = java.lang.System.getProperties();\n" + + "alert(props.get('os.name'));"); + } + actionJavaStatic.title = "Java Static Calls"; + + // show how to access Java classes, methods + function actionJavaAccess() { + showScript(arguments.callee.title, + "// just use new JavaClass();\n" + + "var fr = new javax.swing.JFrame();\n" + + "// call all public methods as in Java\n" + + "fr.setTitle('hello');\n" + + "fr.setDefaultCloseOperation(javax.swing.WindowConstants.DISPOSE_ON_CLOSE);\n" + + "fr.setSize(200, 200);\n" + + "fr.setVisible(true);"); + } + actionJavaAccess.title = "Java Object Access"; + + // show how to use Java bean conventions + function actionJavaBean() { + showScript(arguments.callee.title, + "var fr = new javax.swing.JFrame();\n" + + "fr.setSize(200, 200);\n" + + "// access public get/set methods as fields\n" + + "fr.defaultCloseOperation = javax.swing.WindowConstants.DISPOSE_ON_CLOSE;\n" + + "fr.title = 'hello';\n" + + "fr.visible = true;"); + } + actionJavaBean.title = "Java Beans"; + + // show how to implement Java interface + function actionJavaInterface() { + showScript(arguments.callee.title, + "// use Java anonymizer class-like syntax!\n" + + "var r = new java.lang.Runnable() {\n" + + " run: function() {\n" + + " alert('hello');\n" + + " }\n" + + " };\n" + + "// use the above Runnable to create a Thread\n" + + "new java.lang.Thread(r).start();\n" + + "// For simple one method interfaces, just pass script function\n" + + "new java.lang.Thread(function() { alert('world'); }).start();"); + } + actionJavaInterface.title = "Java Interfaces"; + + // show how to import Java classes, packages + function actionJavaImport() { + showScript(arguments.callee.title, + "// use Java-like import *...\n" + + "// importPackage(java.io);\n" + + "// or import a specific class\n" + + "// importClass(java.io.File);\n" + + "// or better - import just within a scope!\n" + + "var ioPkgs = JavaImporter(java.io);\n" + + "with (ioPkgs) { alert(new File('.').absolutePath); }"); + } + actionJavaImport.title = "Java Import"; + + // "Help" menu + + /* + * Shows a one liner help message for each + * global function. Note that this function + * depends on docString meta-data for each + * function. + */ + function actionHelpGlobals() { + var names = new java.util.ArrayList(); + for (var i in globalThis) { + var func = globalThis[i]; + if (typeof(func) == "function" && + ("docString" in func)) { + names.add(i); + } + } + java.util.Collections.sort(names); + var helpDoc = new java.lang.StringBuffer(); + helpDoc.append(""); + var itr = names.iterator(); + while (itr.hasNext()) { + var name = itr.next(); + helpDoc.append(""); + } + helpDoc.append("
      "); + helpDoc.append(name); + helpDoc.append(""); + helpDoc.append(globalThis[name].docString); + helpDoc.append("
      "); + + var helpEditor = new guiPkgs.JEditorPane(); + helpEditor.setContentType("text/html"); + helpEditor.setEditable(false); + helpEditor.setText(helpDoc.toString()); + + var scroller = new guiPkgs.JScrollPane(); + var port = scroller.getViewport(); + port.add(helpEditor); + + var helpFrame = new guiPkgs.JFrame("Help - Global Functions"); + helpFrame.getContentPane().add("Center", scroller); + helpFrame.setDefaultCloseOperation(guiPkgs.WindowConstants.DISPOSE_ON_CLOSE); + helpFrame.pack(); + helpFrame.setSize(500, 600); + helpFrame.setVisible(true); + } + + // show a simple about message for scriptpad + function actionAbout() { + alert("Scriptpad\nVersion 1.1", "Scriptpad"); + } + + /* + * This data is used to construct menu bar. + * This way adding a menu is easier. Just add + * top level menu or add an item to an existing + * menu. "action" should be a function that is + * called back on clicking the correponding menu. + */ + var menuData = [ + { + menu: "File", + items: [ + { name: "New", action: actionNew , accel: guiPkgs.KeyEvent.VK_N }, + { name: "Open...", action: actionOpen, accel: guiPkgs.KeyEvent.VK_O }, + { name: "Open URL...", action: actionOpenURL, accel: guiPkgs.KeyEvent.VK_U }, + { name: "Save", action: actionSave, accel: guiPkgs.KeyEvent.VK_S }, + { name: "Save As...", action: actionSaveAs }, + { name: "-" }, + { name: "Exit", action: actionExit, accel: guiPkgs.KeyEvent.VK_Q } + ] + }, + + { + menu: "Edit", + items: [ + { name: "Cut", action: actionCut, accel: guiPkgs.KeyEvent.VK_X }, + { name: "Copy", action: actionCopy, accel: guiPkgs.KeyEvent.VK_C }, + { name: "Paste", action: actionPaste, accel: guiPkgs.KeyEvent.VK_V }, + { name: "-" }, + { name: "Select All", action: actionSelectAll, accel: guiPkgs.KeyEvent.VK_A } + ] + }, + + { + menu: "Tools", + items: [ + { name: "Run", action: actionRun, accel: guiPkgs.KeyEvent.VK_R } + ] + }, + + { + menu: "Examples", + items: [ + { name: actionHello.title, action: actionHello }, + { name: actionEval.title, action: actionEval }, + { name: actionJavaStatic.title, action: actionJavaStatic }, + { name: actionJavaAccess.title, action: actionJavaAccess }, + { name: actionJavaBean.title, action: actionJavaBean }, + { name: actionJavaInterface.title, action: actionJavaInterface }, + { name: actionJavaImport.title, action: actionJavaImport } + ] + }, + + { + menu: "Help", + items: [ + { name: "Global Functions", action: actionHelpGlobals }, + { name: "-" }, + { name: "About Scriptpad", action: actionAbout } + ] + } + ]; + + function setMenuAccelerator(mi, accel) { + var keyStroke = guiPkgs.KeyStroke.getKeyStroke(accel, + guiPkgs.Toolkit.getDefaultToolkit().getMenuShortcutKeyMask(), false); + mi.setAccelerator(keyStroke); + } + + // create a menubar using the above menu data + function createMenubar() { + var mb = new guiPkgs.JMenuBar(); + for (var m in menuData) { + var items = menuData[m].items; + var menu = new guiPkgs.JMenu(menuData[m].menu); + + for (var i in items) { + if (items[i].name.equals("-")) { + menu.addSeparator(); + } else { + var mi = new guiPkgs.JMenuItem(items[i].name); + var action = items[i].action; + mi.addActionListener(action); + var accel = items[i].accel; + if (accel) { + setMenuAccelerator(mi, accel); + } + menu.add(mi); + } + } + + mb.add(menu); + } + + return mb; + } + + // function to add a new menu item under "Tools" menu + function addTool(menuItem, action, accel) { + if (typeof(action) != "function") { + return; + } + + var toolsIndex = -1; + // find the index of the "Tools" menu + for (var i in menuData) { + if (menuData[i].menu.equals("Tools")) { + toolsIndex = i; + break; + } + } + if (toolsIndex == -1) { + return; + } + var toolsMenu = frame.getJMenuBar().getMenu(toolsIndex); + var mi = new guiPkgs.JMenuItem(menuItem); + mi.addActionListener(action); + if (accel) { + setMenuAccelerator(mi, accel); + } + toolsMenu.add(mi); + } + + // create Scriptpad frame + function createFrame() { + frame = new guiPkgs.JFrame(); + frame.setTitle(defaultTitle); + frame.setBackground(guiPkgs.Color.lightGray); + frame.getContentPane().setLayout(new guiPkgs.BorderLayout()); + + // create notepad panel + var notepad = new guiPkgs.JPanel(); + notepad.setBorder(guiPkgs.BorderFactory.createEtchedBorder()); + notepad.setLayout(new guiPkgs.BorderLayout()); + + // create editor + editor = createEditor(); + var scroller = new guiPkgs.JScrollPane(); + var port = scroller.getViewport(); + port.add(editor); + + // add editor to notepad panel + var panel = new guiPkgs.JPanel(); + panel.setLayout(new guiPkgs.BorderLayout()); + panel.add("Center", scroller); + notepad.add("Center", panel); + + // add notepad panel to frame + frame.getContentPane().add("Center", notepad); + + // set menu bar to frame and show the frame + frame.setJMenuBar(createMenubar()); + frame.setDefaultCloseOperation(guiPkgs.JFrame.EXIT_ON_CLOSE); + frame.pack(); + frame.setSize(500, 600); + } + + // show Scriptpad frame + function showFrame() { + // set global variable by the name "window" + globalThis.window = frame; + + // open new document + actionNew(); + + frame.setVisible(true); + } + + // create and show Scriptpad frame + createFrame(); + showFrame(); + + /* + * Application object has two fields "frame", "editor" + * which are current JFrame and editor and a method + * called "addTool" to add new menu item to "Tools" menu. + */ + return { + frame: frame, + editor: editor, + addTool: addTool + }; +}; /* - * Call the main and store Application object + * Call the main and store Application object * in a global variable named "application". */ var application = main(); @@ -657,4 +660,3 @@ function loadUserInit() { } loadUserInit(); - diff --git a/jdk/src/share/sample/scripting/scriptpad/src/scripts/browse.js b/jdk/src/share/sample/scripting/scriptpad/src/scripts/browse.js index 975e45435e3..fbe50e1a414 100644 --- a/jdk/src/share/sample/scripting/scriptpad/src/scripts/browse.js +++ b/jdk/src/share/sample/scripting/scriptpad/src/scripts/browse.js @@ -1,5 +1,5 @@ /* - * Copyright (c) 2006, 2011, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2006, 2013, Oracle and/or its affiliates. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -37,33 +37,30 @@ * this sample code. */ - /* * This function uses new Swing Desktop API in JDK 6. * To use this with scriptpad, open this in scriptpad * and use "Tools->Run Script" menu. */ function browse() { - with (guiPkgs) { - var desktop = null; - // Before more Desktop API is used, first check - // whether the API is supported by this particular - // virtual machine (VM) on this particular host. - if (Desktop.isDesktopSupported()) { - desktop = Desktop.getDesktop(); - } else { - alert("no desktop support"); - return; - } + var desktop = null; + // Before more Desktop API is used, first check + // whether the API is supported by this particular + // virtual machine (VM) on this particular host. + if (java.awt.Desktop.isDesktopSupported()) { + desktop = java.awt.Desktop.getDesktop(); + } else { + alert("no desktop support"); + return; + } - if (desktop.isSupported(Desktop.Action.BROWSE)) { - var url = prompt("Address:"); - if (url != null) { - desktop.browse(new java.net.URI(url)); - } - } else { - alert("no browser support"); + if (desktop.isSupported(java.awt.Desktop.Action.BROWSE)) { + var url = prompt("Address:"); + if (url != null) { + desktop.browse(new java.net.URI(url)); } + } else { + alert("no browser support"); } } @@ -71,4 +68,3 @@ if (this.application != undefined) { // add "Browse" menu item under "Tools" menu this.application.addTool("Browse", browse); } - diff --git a/jdk/src/share/sample/scripting/scriptpad/src/scripts/insertfile.js b/jdk/src/share/sample/scripting/scriptpad/src/scripts/insertfile.js index b463e9a79c7..07a7ac28d19 100644 --- a/jdk/src/share/sample/scripting/scriptpad/src/scripts/insertfile.js +++ b/jdk/src/share/sample/scripting/scriptpad/src/scripts/insertfile.js @@ -1,5 +1,5 @@ /* - * Copyright (c) 2006, 2011, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2006, 2013, Oracle and/or its affiliates. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -37,7 +37,6 @@ * this sample code. */ - /* * This script adds "Insert File" mode menu item to "Tools" menu. * When selected, this menu shows a file dialog box and inserts @@ -48,18 +47,22 @@ if (this.application) { application.addTool("Insert File...", function() { var file = fileDialog(); - if (file) { + + if (file) { var reader = new java.io.FileReader(file); var arr = java.lang.reflect.Array.newInstance( java.lang.Character.TYPE, 8*1024); // 8K at a time var buf = new java.lang.StringBuffer(); var numChars; + while ((numChars = reader.read(arr, 0, arr.length)) > 0) { buf.append(arr, 0, numChars); } + var pos = application.editor.caretPosition; var doc = application.editor.document; + doc.insertString(pos, buf.toString(), null); } - }) + }); } diff --git a/jdk/src/share/sample/scripting/scriptpad/src/scripts/linewrap.js b/jdk/src/share/sample/scripting/scriptpad/src/scripts/linewrap.js index e1ac50dc126..989473a6cf1 100644 --- a/jdk/src/share/sample/scripting/scriptpad/src/scripts/linewrap.js +++ b/jdk/src/share/sample/scripting/scriptpad/src/scripts/linewrap.js @@ -1,5 +1,5 @@ /* - * Copyright (c) 2006, 2011, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2006, 2013, Oracle and/or its affiliates. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -37,19 +37,15 @@ * this sample code. */ - /* * This script adds "Line Wrap" mode menu item to "Tools" menu. * When selected, this menu toggles the current word wrap mode * of the editor. */ -with (guiPkgs) { - function toggleLineWrap() { - var wrap = application.editor.lineWrap; - application.editor.lineWrap = !wrap; - } - - application.addTool("Line Wrap", toggleLineWrap); +function toggleLineWrap() { + var wrap = application.editor.lineWrap; + application.editor.lineWrap = !wrap; } +application.addTool("Line Wrap", toggleLineWrap); diff --git a/jdk/src/share/sample/scripting/scriptpad/src/scripts/mail.js b/jdk/src/share/sample/scripting/scriptpad/src/scripts/mail.js index 7104e29ad1d..cabe5c47541 100644 --- a/jdk/src/share/sample/scripting/scriptpad/src/scripts/mail.js +++ b/jdk/src/share/sample/scripting/scriptpad/src/scripts/mail.js @@ -1,5 +1,5 @@ /* - * Copyright (c) 2006, 2011, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2006, 2013, Oracle and/or its affiliates. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -37,30 +37,27 @@ * this sample code. */ - /* * This function uses new Swing Desktop API in JDK 6. * To use this with scriptpad, open this in scriptpad * and use "Tools->Run Script" menu. */ function mail() { - with (guiPkgs) { - var desktop = null; - // Before more Desktop API is used, first check - // whether the API is supported by this particular - // virtual machine (VM) on this particular host. - if (Desktop.isDesktopSupported()) { - desktop = Desktop.getDesktop(); - } else { - alert("no desktop support"); - return; - } + var desktop = null; + // Before more Desktop API is used, first check + // whether the API is supported by this particular + // virtual machine (VM) on this particular host. + if (java.awt.Desktop.isDesktopSupported()) { + desktop = java.awt.Desktop.getDesktop(); + } else { + alert("no desktop support"); + return; + } - if (desktop.isSupported(Desktop.Action.MAIL)) { - var mailTo = prompt("Mail To:"); - if (mailTo != null) { - desktop.mail(new java.net.URI("mailto", mailTo, null)); - } + if (desktop.isSupported(java.awt.Desktop.Action.MAIL)) { + var mailTo = prompt("Mail To:"); + if (mailTo != null) { + desktop.mail(new java.net.URI("mailto", mailTo, null)); } } } diff --git a/jdk/src/share/sample/scripting/scriptpad/src/scripts/memmonitor.js b/jdk/src/share/sample/scripting/scriptpad/src/scripts/memmonitor.js index 7fac72f6954..0744e303b78 100644 --- a/jdk/src/share/sample/scripting/scriptpad/src/scripts/memmonitor.js +++ b/jdk/src/share/sample/scripting/scriptpad/src/scripts/memmonitor.js @@ -1,5 +1,5 @@ /* - * Copyright (c) 2006, 2011, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2006, 2013, Oracle and/or its affiliates. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -37,36 +37,37 @@ * this sample code. */ - // this checker function runs asynchronously function memoryChecker(memoryBean, threshold, interval) { while (true) { var memUsage = memoryBean.HeapMemoryUsage; var usage = memUsage.get("used") / (1024 * 1024); - println(usage); + + println("usage: " + usage); + if (usage > threshold) { alert("Hey! heap usage threshold exceeded!"); // after first alert just return. return; } - java.lang.Thread.currentThread().sleep(interval); + + java.lang.Thread.sleep(interval); } } - // add "Tools->Memory Monitor" menu item if (this.application != undefined) { - this.application.addTool("Memory Monitor", + this.application.addTool("Memory Monitor", function () { // show threshold box with default of 50 MB var threshold = prompt("Threshold (mb)", 50); + // show interval box with default of 1000 millisec. var interval = prompt("Sample Interval (ms):", 1000); var memoryBean = mbean("java.lang:type=Memory"); - // ".future" makes the function to be called + // ".future" makes the function to be called // asynchronously in a separate thread. memoryChecker.future(memoryBean, threshold, interval); }); } - diff --git a/jdk/src/share/sample/scripting/scriptpad/src/scripts/memory.js b/jdk/src/share/sample/scripting/scriptpad/src/scripts/memory.js index 53b8b3575b7..b8252fbfbcc 100644 --- a/jdk/src/share/sample/scripting/scriptpad/src/scripts/memory.js +++ b/jdk/src/share/sample/scripting/scriptpad/src/scripts/memory.js @@ -1,5 +1,5 @@ /* - * Copyright (c) 2006, 2011, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2006, 2013, Oracle and/or its affiliates. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -37,7 +37,6 @@ * this sample code. */ - /* * This script serves as a simple "monitored application". * Start this script using memory.bat or memory.sh in the @@ -45,12 +44,11 @@ */ java.lang.System.out.print("Enter a number and press enter:"); -java.lang.System["in"].read(); +var input = java.lang.System["in"].read(); // allocate an integer array of "big enough" size! var a = java.lang.reflect.Array.newInstance( - java.lang.Integer.TYPE, 1024*1024); - -// loop forever! -while (true); + java.lang.Integer.TYPE, input * 1024 * 1024); +// sleep some time... +java.lang.Thread.sleep(10*60*1000); diff --git a/jdk/src/share/sample/scripting/scriptpad/src/scripts/memory.sh b/jdk/src/share/sample/scripting/scriptpad/src/scripts/memory.sh index d42370ffe6e..5477623c8ee 100644 --- a/jdk/src/share/sample/scripting/scriptpad/src/scripts/memory.sh +++ b/jdk/src/share/sample/scripting/scriptpad/src/scripts/memory.sh @@ -30,7 +30,3 @@ # jrunscript -J-Dcom.sun.management.jmxremote.port=1090 -J-Dcom.sun.management.jmxremote.ssl=false -J-Dcom.sun.management.jmxremote.authenticate=false memory.js - - - - diff --git a/jdk/src/share/sample/scripting/scriptpad/src/scripts/textcolor.js b/jdk/src/share/sample/scripting/scriptpad/src/scripts/textcolor.js index 49f5dfbf1f5..b9d86cc37f0 100644 --- a/jdk/src/share/sample/scripting/scriptpad/src/scripts/textcolor.js +++ b/jdk/src/share/sample/scripting/scriptpad/src/scripts/textcolor.js @@ -1,5 +1,5 @@ /* - * Copyright (c) 2006, 2011, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2006, 2013, Oracle and/or its affiliates. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions @@ -37,14 +37,13 @@ * this sample code. */ - /* * This script adds "Selected Text Color" menu item to "Tools" menu. * When selected, this menu changes the "selected text" color. */ if (this.application) { application.addTool("Selected Text Color...", - function() { + function() { var color = application.editor.selectedTextColor; color = colorDialog("Selected Text Color", color); application.editor.selectedTextColor = color; diff --git a/jdk/src/windows/classes/java/lang/ProcessImpl.java b/jdk/src/windows/classes/java/lang/ProcessImpl.java index 90878af2d10..de7a9057c54 100644 --- a/jdk/src/windows/classes/java/lang/ProcessImpl.java +++ b/jdk/src/windows/classes/java/lang/ProcessImpl.java @@ -37,7 +37,10 @@ import java.io.BufferedOutputStream; import java.lang.ProcessBuilder.Redirect; import java.security.AccessController; import java.security.PrivilegedAction; +import java.util.ArrayList; import java.util.concurrent.TimeUnit; +import java.util.regex.Matcher; +import java.util.regex.Pattern; /* This class is for the exclusive use of ProcessBuilder.start() to * create new processes. @@ -145,6 +148,66 @@ final class ProcessImpl extends Process { } + private static class LazyPattern { + // Escape-support version: + // "(\")((?:\\\\\\1|.)+?)\\1|([^\\s\"]+)"; + private static final Pattern PATTERN = + Pattern.compile("[^\\s\"]+|\"[^\"]*\""); + }; + + /* Parses the command string parameter into the executable name and + * program arguments. + * + * The command string is broken into tokens. The token separator is a space + * or quota character. The space inside quotation is not a token separator. + * There are no escape sequences. + */ + private static String[] getTokensFromCommand(String command) { + ArrayList matchList = new ArrayList<>(8); + Matcher regexMatcher = LazyPattern.PATTERN.matcher(command); + while (regexMatcher.find()) + matchList.add(regexMatcher.group()); + return matchList.toArray(new String[matchList.size()]); + } + + private static String createCommandLine(boolean isCmdFile, + final String executablePath, + final String cmd[]) + { + StringBuilder cmdbuf = new StringBuilder(80); + + cmdbuf.append(executablePath); + + for (int i = 1; i < cmd.length; ++i) { + cmdbuf.append(' '); + String s = cmd[i]; + if (needsEscaping(isCmdFile, s)) { + cmdbuf.append('"'); + cmdbuf.append(s); + + // The code protects the [java.exe] and console command line + // parser, that interprets the [\"] combination as an escape + // sequence for the ["] char. + // http://msdn.microsoft.com/en-us/library/17w5ykft.aspx + // + // If the argument is an FS path, doubling of the tail [\] + // char is not a problem for non-console applications. + // + // The [\"] sequence is not an escape sequence for the [cmd.exe] + // command line parser. The case of the [""] tail escape + // sequence could not be realized due to the argument validation + // procedure. + if (!isCmdFile && s.endsWith("\\")) { + cmdbuf.append('\\'); + } + cmdbuf.append('"'); + } else { + cmdbuf.append(s); + } + } + return cmdbuf.toString(); + } + // We guarantee the only command file execution for implicit [cmd.exe] run. // http://technet.microsoft.com/en-us/library/bb490954.aspx private static final char CMD_BAT_ESCAPE[] = {' ', '\t', '<', '>', '&', '|', '^'}; @@ -227,64 +290,89 @@ final class ProcessImpl extends Process { } + private boolean isShellFile(String executablePath) { + String upPath = executablePath.toUpperCase(); + return (upPath.endsWith(".CMD") || upPath.endsWith(".BAT")); + } + + private String quoteString(String arg) { + StringBuilder argbuf = new StringBuilder(arg.length() + 2); + return argbuf.append('"').append(arg).append('"').toString(); + } + + private long handle = 0; private OutputStream stdin_stream; private InputStream stdout_stream; private InputStream stderr_stream; - private ProcessImpl(final String cmd[], + private ProcessImpl(String cmd[], final String envblock, final String path, final long[] stdHandles, final boolean redirectErrorStream) throws IOException { - // The [executablePath] is not quoted for any case. - String executablePath = getExecutablePath(cmd[0]); - - // We need to extend the argument verification procedure - // to guarantee the only command file execution for implicit [cmd.exe] - // run. - String upPath = executablePath.toUpperCase(); - boolean isCmdFile = (upPath.endsWith(".CMD") || upPath.endsWith(".BAT")); - - StringBuilder cmdbuf = new StringBuilder(80); - - // Quotation protects from interpretation of the [path] argument as - // start of longer path with spaces. Quotation has no influence to - // [.exe] extension heuristic. - cmdbuf.append('"'); - cmdbuf.append(executablePath); - cmdbuf.append('"'); - - for (int i = 1; i < cmd.length; i++) { - cmdbuf.append(' '); - String s = cmd[i]; - if (needsEscaping(isCmdFile, s)) { - cmdbuf.append('"'); - cmdbuf.append(s); - - // The code protects the [java.exe] and console command line - // parser, that interprets the [\"] combination as an escape - // sequence for the ["] char. - // http://msdn.microsoft.com/en-us/library/17w5ykft.aspx - // - // If the argument is an FS path, doubling of the tail [\] - // char is not a problem for non-console applications. - // - // The [\"] sequence is not an escape sequence for the [cmd.exe] - // command line parser. The case of the [""] tail escape - // sequence could not be realized due to the argument validation - // procedure. - if (!isCmdFile && s.endsWith("\\")) { - cmdbuf.append('\\'); - } - cmdbuf.append('"'); - } else { - cmdbuf.append(s); - } + String cmdstr; + SecurityManager security = System.getSecurityManager(); + boolean allowAmbigousCommands = false; + if (security == null) { + String value = System.getProperty("jdk.lang.Process.allowAmbigousCommands"); + if (value != null) + allowAmbigousCommands = !"false".equalsIgnoreCase(value); + } + if (allowAmbigousCommands) { + // Legacy mode. + + // Normalize path if possible. + String executablePath = new File(cmd[0]).getPath(); + + // No worry about internal and unpaired ["] . + if (needsEscaping(false, executablePath) ) + executablePath = quoteString(executablePath); + + cmdstr = createCommandLine( + false, //legacy mode doesn't worry about extended verification + executablePath, + cmd); + } else { + String executablePath; + try { + executablePath = getExecutablePath(cmd[0]); + } catch (IllegalArgumentException e) { + // Workaround for the calls like + // Runtime.getRuntime().exec("\"C:\\Program Files\\foo\" bar") + + // No chance to avoid CMD/BAT injection, except to do the work + // right from the beginning. Otherwise we have too many corner + // cases from + // Runtime.getRuntime().exec(String[] cmd [, ...]) + // calls with internal ["] and escape sequences. + + // Restore original command line. + StringBuilder join = new StringBuilder(); + // terminal space in command line is ok + for (String s : cmd) + join.append(s).append(' '); + + // Parse the command line again. + cmd = getTokensFromCommand(join.toString()); + executablePath = getExecutablePath(cmd[0]); + + // Check new executable name once more + if (security != null) + security.checkExec(executablePath); + } + + // Quotation protects from interpretation of the [path] argument as + // start of longer path with spaces. Quotation has no influence to + // [.exe] extension heuristic. + cmdstr = createCommandLine( + // We need the extended verification procedure for CMD files. + isShellFile(executablePath), + quoteString(executablePath), + cmd); } - String cmdstr = cmdbuf.toString(); handle = create(cmdstr, envblock, path, stdHandles, redirectErrorStream); diff --git a/jdk/test/java/lang/Runtime/exec/ExecCommand.java b/jdk/test/java/lang/Runtime/exec/ExecCommand.java new file mode 100644 index 00000000000..40f61734e56 --- /dev/null +++ b/jdk/test/java/lang/Runtime/exec/ExecCommand.java @@ -0,0 +1,163 @@ +/* + * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + + +/** + * @test + * @bug 8012453 + * @run main/othervm ExecCommand + * @summary workaround for legacy applications with Runtime.getRuntime().exec(String command) + */ + +import java.io.BufferedWriter; +import java.io.File; +import java.io.FileWriter; +import java.io.IOException; +import java.security.AccessControlException; + +public class ExecCommand { + static class SecurityMan extends SecurityManager { + public static String unquote(String str) + { + int length = (str == null) + ? 0 + : str.length(); + + if (length > 1 + && str.charAt(0) == '\"' + && str.charAt(length - 1) == '\"') + { + return str.substring(1, length - 1); + } + return str; + } + + @Override public void checkExec(String cmd) { + String ncmd = (new File(unquote(cmd))).getPath(); + if ( ncmd.equals(".\\Program") + || ncmd.equals("\".\\Program") + || ncmd.equals(".\\Program Files\\do.cmd") + || ncmd.equals(".\\Program.cmd")) + { + return; + } + super.checkExec(cmd); + } + } + + // Parameters for the Runtime.exec calls + private static final String TEST_RTE_ARG[] = { + ".\\Program Files\\do.cmd", + "\".\\Program Files\\doNot.cmd\" arg", + "\".\\Program Files\\do.cmd\" arg", + // compatibility + "\".\\Program.cmd\" arg", + ".\\Program.cmd arg", + }; + + private static final String doCmdCopy[] = { + ".\\Program.cmd", + ".\\Program Files\\doNot.cmd", + ".\\Program Files\\do.cmd", + }; + + // Golden image for results + private static final String TEST_RTE_GI[][] = { + //Pure system | Legacy mode | Legacy mode & SM + // [.\Program File\do.cmd] + new String[]{"IOException", // [.\Program] not found + "Success", + "IOException"}, //SM - no legacy mode [.\Program] - OK + + // [".\Program File\doNot.cmd" arg] + new String[]{"Success", + "Success", + "AccessControlException"}, //SM - [".\Program] - OK, + // [.\\Program Files\\doNot.cmd] - Fail + + // [".\Program File\do.cmd" arg] + // AccessControlException + new String[]{"Success", + "Success", + "Success"}, //SM - [".\Program] - OK, + // [.\\Program Files\\do.cmd] - OK + + // compatibility + new String[]{"Success", "Success", "Success"}, //[".\Program.cmd"] + new String[]{"Success", "Success", "Success"} //[.\Program.cmd] + }; + + public static void main(String[] _args) throws Exception { + if (!System.getProperty("os.name").startsWith("Windows")) { + return; + } + + // tear up + try { + new File(".\\Program Files").mkdirs(); + for (int i = 0; i < doCmdCopy.length; ++i) { + try (BufferedWriter outCmd = new BufferedWriter( + new FileWriter(doCmdCopy[i]))) { + outCmd.write("@echo %1"); + } + } + } catch (IOException e) { + throw new Error(e.getMessage()); + } + + // action + for (int k = 0; k < 3; ++k) { + switch (k) { + case 1: + System.setProperty("jdk.lang.Process.allowAmbigousCommands", ""); + break; + case 2: + System.setSecurityManager( new SecurityMan() ); + break; + } + for (int i = 0; i < TEST_RTE_ARG.length; ++i) { + String outRes; + try { + Process exec = Runtime.getRuntime().exec(TEST_RTE_ARG[i]); + exec.waitFor(); + outRes = "Success"; + } catch (IOException ioe) { + outRes = "IOException: " + ioe.getMessage(); + } catch (IllegalArgumentException iae) { + outRes = "IllegalArgumentException: " + iae.getMessage(); + } catch (AccessControlException se) { + outRes = "AccessControlException: " + se.getMessage(); + } + + if (!outRes.startsWith(TEST_RTE_GI[i][k])) { + throw new Error("Unexpected output! Step" + k + "" + i + + " \nArgument: " + TEST_RTE_ARG[i] + + "\nExpected: " + TEST_RTE_GI[i][k] + + "\n Output: " + outRes); + } else { + System.out.println("RTE OK:" + TEST_RTE_ARG[i]); + } + } + } + } +} diff --git a/jdk/test/java/lang/StringBuffer/ToStringCache.java b/jdk/test/java/lang/StringBuffer/ToStringCache.java new file mode 100644 index 00000000000..6259e27d0e3 --- /dev/null +++ b/jdk/test/java/lang/StringBuffer/ToStringCache.java @@ -0,0 +1,289 @@ +/* + * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* @test + * @bug 8013395 + * @summary Test StringBuffer.toString caching + */ + +public class ToStringCache { + + // we can't test that we actually use a cached value (the benchmarks + // verify that) but we have to test that the cache is cleared when + // expected + + public static void main(String[] args) throws Exception { + String original = "The original String"; + + StringBuffer sb = new StringBuffer(original); + + String a = sb.toString(); + checkEqual(a, original); + + String b = sb.toString(); + checkEqual(a, b); + + // mutating methods + + sb.setLength(12); + b = sb.toString(); + checkUnequal(a, b); + a = b; + + sb.setCharAt(0, 'X'); + b = sb.toString(); + checkUnequal(a, b); + a = b; + + sb.append(new Character('X')); + b = sb.toString(); + checkUnequal(a, b); + a = b; + + sb.append("More text"); + b = sb.toString(); + checkUnequal(a, b); + a = b; + + sb.append(sb); + b = sb.toString(); + checkUnequal(a, b); + a = b; + + sb.append(new StringBuilder("Build")); + b = sb.toString(); + checkUnequal(a, b); + a = b; + + sb.append(new StringBuilder("Build2"), 0, 1); + b = sb.toString(); + checkUnequal(a, b); + a = b; + + sb.append(new char[] { 'a', 'b' }); + b = sb.toString(); + checkUnequal(a, b); + a = b; + + sb.append(true); + b = sb.toString(); + checkUnequal(a, b); + a = b; + + sb.append('c'); + b = sb.toString(); + checkUnequal(a, b); + a = b; + + sb.append(23); + b = sb.toString(); + checkUnequal(a, b); + a = b; + + sb.appendCodePoint(Character.codePointAt(new char[] { 'X'}, 0)); + b = sb.toString(); + checkUnequal(a, b); + a = b; + + sb.append(1L); + b = sb.toString(); + checkUnequal(a, b); + a = b; + + sb.append(1.0f); + b = sb.toString(); + checkUnequal(a, b); + a = b; + + sb.append(1.0d); + b = sb.toString(); + checkUnequal(a, b); + a = b; + + sb.delete(0, 5); + b = sb.toString(); + checkUnequal(a, b); + a = b; + + sb.deleteCharAt(0); + b = sb.toString(); + checkUnequal(a, b); + a = b; + + sb.replace(0,2, "123"); + b = sb.toString(); + checkUnequal(a, b); + a = b; + + sb.insert(0, new char[] { 'a', 'b', 'c'}, 0, 3); + b = sb.toString(); + checkUnequal(a, b); + a = b; + + sb.insert(0, new Object()); + b = sb.toString(); + checkUnequal(a, b); + a = b; + + sb.insert(0, "abc"); + b = sb.toString(); + checkUnequal(a, b); + a = b; + + sb.insert(0, new char[] { 'a', 'b', 'c' }); + b = sb.toString(); + checkUnequal(a, b); + a = b; + + sb.insert(0, new StringBuilder("Build")); + b = sb.toString(); + checkUnequal(a, b); + a = b; + + sb.insert(0, new StringBuilder("Build"), 0, 1); + b = sb.toString(); + checkUnequal(a, b); + a = b; + + sb.insert(0, false); + b = sb.toString(); + checkUnequal(a, b); + a = b; + + sb.insert(0, 'X'); + b = sb.toString(); + checkUnequal(a, b); + a = b; + + sb.insert(0, 1); + b = sb.toString(); + checkUnequal(a, b); + a = b; + + sb.insert(0, 1L); + b = sb.toString(); + checkUnequal(a, b); + a = b; + + sb.insert(0, 1.0f); + b = sb.toString(); + checkUnequal(a, b); + a = b; + + sb.insert(0, 1.0d); + b = sb.toString(); + checkUnequal(a, b); + a = b; + + sb.reverse(); + b = sb.toString(); + checkUnequal(a, b); + + // non-mutating methods + + // Reset to known value + sb = new StringBuffer(original); + a = sb.toString(); + b = sb.toString(); + checkEqual(a, b); + + int l = sb.length(); + b = sb.toString(); + checkEqual(a, b); + + int cap = sb.capacity(); + b = sb.toString(); + checkEqual(a, b); + + sb.ensureCapacity(100); + b = sb.toString(); + checkEqual(a, b); + + sb.trimToSize(); + b = sb.toString(); + checkEqual(a, b); + + char c = sb.charAt(1); + b = sb.toString(); + checkEqual(a, b); + + int cp = sb.codePointAt(1); + b = sb.toString(); + checkEqual(a, b); + + cp = sb.codePointBefore(2); + b = sb.toString(); + checkEqual(a, b); + + int count = sb.codePointCount(0,1); + b = sb.toString(); + checkEqual(a, b); + + count = sb.offsetByCodePoints(0, 1); + b = sb.toString(); + checkEqual(a, b); + + sb.getChars(0, 1, new char[2], 0); + b = sb.toString(); + checkEqual(a, b); + + String sub = sb.substring(0); + b = sb.toString(); + checkEqual(a, b); + + CharSequence cs = sb.subSequence(0,1); + b = sb.toString(); + checkEqual(a, b); + + sub = sb.substring(0, 3); + b = sb.toString(); + checkEqual(a, b); + + int index = sb.indexOf("rig"); + b = sb.toString(); + checkEqual(a, b); + + index = sb.indexOf("rig", 2); + b = sb.toString(); + checkEqual(a, b); + + index = sb.lastIndexOf("rig"); + b = sb.toString(); + checkEqual(a, b); + + index = sb.lastIndexOf("rig", 3); + b = sb.toString(); + checkEqual(a, b); + + } + + private static void checkEqual(String s1, String s2) { + if (!s1.equals(s2)) + throw new RuntimeException("Unmatched strings: s1 = " + + s1 + " s2 = " + s2); + } + private static void checkUnequal(String s1, String s2) { + if (s1.equals(s2)) + throw new RuntimeException("Unexpected matched strings: " + s1); + } +} diff --git a/jdk/test/java/net/HttpURLPermission/HttpURLPermissionTest.java b/jdk/test/java/net/HttpURLPermission/HttpURLPermissionTest.java new file mode 100644 index 00000000000..c2debe7a0bd --- /dev/null +++ b/jdk/test/java/net/HttpURLPermission/HttpURLPermissionTest.java @@ -0,0 +1,203 @@ +/* + * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +import java.net.HttpURLPermission; +import java.io.*; + +/** + * @test + * @bug 8010464 + */ + +public class HttpURLPermissionTest { + + // super class for all test types + abstract static class Test { + boolean expected; + abstract boolean execute(); + }; + + // Tests URL part of implies() method. This is the main test. + static class URLImpliesTest extends Test { + String arg1, arg2; + + URLImpliesTest(String arg1, String arg2, boolean expected) { + this.arg1 = arg1; + this.arg2 = arg2; + this.expected = expected; + } + + boolean execute() { + HttpURLPermission p1 = new HttpURLPermission (arg1, "GET:*"); + HttpURLPermission p2 = new HttpURLPermission (arg2, "GET:*"); + boolean result = p1.implies(p2); + return result == expected; + } + }; + + static URLImpliesTest imtest(String arg1, String arg2, boolean expected) { + return new URLImpliesTest(arg1, arg2, expected); + } + + static class ActionImpliesTest extends Test { + String arg1, arg2; + + ActionImpliesTest(String arg1, String arg2, boolean expected) { + this.arg1 = arg1; + this.arg2 = arg2; + this.expected = expected; + } + + boolean execute() { + String url1 = "http://www.foo.com/-"; + String url2 = "http://www.foo.com/a/b"; + HttpURLPermission p1 = new HttpURLPermission(url1, arg1); + HttpURLPermission p2 = new HttpURLPermission(url2, arg2); + boolean result = p1.implies(p2); + return result == expected; + } + } + + static ActionImpliesTest actest(String arg1, String arg2, boolean expected) { + return new ActionImpliesTest(arg1, arg2, expected); + } + + static Test[] pathImplies = { + // single + imtest("http://www.foo.com/", "http://www.foo.com/", true), + imtest("http://www.bar.com/", "http://www.foo.com/", false), + imtest("http://www.foo.com/a/b", "http://www.foo.com/", false), + imtest("http://www.foo.com/a/b", "http://www.foo.com/a/b/c", false), + // wildcard + imtest("http://www.foo.com/a/b/*", "http://www.foo.com/a/b/c", true), + imtest("http://www.foo.com/a/b/*", "http://www.foo.com/a/b/*", true), + imtest("http://www.foo.com/a/b/*", "http://www.foo.com/a/b/c#frag", true), + imtest("http://www.foo.com/a/b/*", "http://www.foo.com/a/b/c#frag?foo=foo", true), + imtest("http://www.foo.com/a/b/*", "http://www.foo.com/b/b/c", false), + imtest("http://www.foo.com/a/b/*", "http://www.foo.com/a/b/c.html", true), + imtest("http://www.foo.com/a/b/*", "http://www.foo.com/a/b/c.html", true), + imtest("http://www.foo.com/a/b/*", "https://www.foo.com/a/b/c", false), + // recursive + imtest("http://www.foo.com/a/b/-", "http://www.foo.com/a/b/-", true), + imtest("http://www.foo.com/a/b/-", "http://www.foo.com/a/b/c", true), + imtest("http://www.foo.com/a/b/-", "http://www.foo.com/a/b/c#frag", true), + imtest("http://www.foo.com/a/b/-", "http://www.foo.com/a/b/c#frag?foo=foo", true), + imtest("http://www.foo.com/a/b/-", "http://www.foo.com/b/b/c", false), + imtest("http://www.foo.com/a/b/-", "http://www.foo.com/a/b/c.html", true), + imtest("http://www.foo.com/a/b/-", "http://www.foo.com/a/b/c.html", true), + imtest("http://www.foo.com/a/b/-", "http://www.foo.com/a/b/c/d/e.html", true), + imtest("https://www.foo.com/a/b/-", "http://www.foo.com/a/b/c/d/e.html", false), + imtest("http://www.foo.com/a/b/-", "http://www.foo.com/a/b/c/d/e#frag", true), + imtest("http://www.foo.com/a/b/-", "https://www.foo.com/a/b/c", false), + // special cases + imtest("http:*", "https://www.foo.com/a/b/c", false), + imtest("http:*", "http://www.foo.com/a/b/c", true), + imtest("http:*", "http://foo/bar", true), + imtest("http://foo/bar", "https://foo/bar", false) + }; + + static Test[] actionImplies = { + actest("GET", "GET", true), + actest("GET", "POST", false), + actest("GET:", "PUT", false), + actest("GET:", "GET", true), + actest("GET,POST", "GET", true), + actest("GET,POST:", "GET", true), + actest("GET:X-Foo", "GET:x-foo", true), + actest("GET:X-Foo,X-bar", "GET:x-foo", true), + actest("GET:X-Foo", "GET:x-boo", false), + actest("GET:X-Foo,X-Bar", "GET:x-bar,x-foo", true), + actest("GET:X-Bar,X-Foo,X-Bar,Y-Foo", "GET:x-bar,x-foo", true), + actest("GET:*", "GET:x-bar,x-foo", true), + actest("*:*", "GET:x-bar,x-foo", true) + }; + + static boolean failed = false; + + public static void main(String args[]) throws Exception { + for (int i=0; i set = EnumSet.range(WEEKS, FOREVER); + EnumSet set = EnumSet.range(DAYS, FOREVER); INVALID_UNITS = (TemporalUnit[]) set.toArray(new TemporalUnit[set.size()]); } @@ -1122,14 +1122,6 @@ public class TCKLocalTime extends AbstractDateTimeTest { } } - @Test - public void test_plus_longTemporalUnit_multiples() { - assertEquals(TEST_12_30_40_987654321.plus(0, DAYS), TEST_12_30_40_987654321); - assertEquals(TEST_12_30_40_987654321.plus(1, DAYS), TEST_12_30_40_987654321); - assertEquals(TEST_12_30_40_987654321.plus(2, DAYS), TEST_12_30_40_987654321); - assertEquals(TEST_12_30_40_987654321.plus(-3, DAYS), TEST_12_30_40_987654321); - } - @Test(expectedExceptions=NullPointerException.class) public void test_plus_longTemporalUnit_null() { TEST_12_30_40_987654321.plus(1, (TemporalUnit) null); @@ -1556,14 +1548,6 @@ public class TCKLocalTime extends AbstractDateTimeTest { } } - @Test - public void test_minus_longTemporalUnit_long_multiples() { - assertEquals(TEST_12_30_40_987654321.minus(0, DAYS), TEST_12_30_40_987654321); - assertEquals(TEST_12_30_40_987654321.minus(1, DAYS), TEST_12_30_40_987654321); - assertEquals(TEST_12_30_40_987654321.minus(2, DAYS), TEST_12_30_40_987654321); - assertEquals(TEST_12_30_40_987654321.minus(-3, DAYS), TEST_12_30_40_987654321); - } - @Test(expectedExceptions=NullPointerException.class) public void test_minus_longTemporalUnit_null() { TEST_12_30_40_987654321.minus(1, (TemporalUnit) null); diff --git a/jdk/test/java/time/tck/java/time/TCKOffsetTime.java b/jdk/test/java/time/tck/java/time/TCKOffsetTime.java index ff8ec88502e..7b97380e4de 100644 --- a/jdk/test/java/time/tck/java/time/TCKOffsetTime.java +++ b/jdk/test/java/time/tck/java/time/TCKOffsetTime.java @@ -75,7 +75,13 @@ import static java.time.temporal.ChronoField.NANO_OF_SECOND; import static java.time.temporal.ChronoField.OFFSET_SECONDS; import static java.time.temporal.ChronoField.SECOND_OF_DAY; import static java.time.temporal.ChronoField.SECOND_OF_MINUTE; +import static java.time.temporal.ChronoUnit.MONTHS; import static java.time.temporal.ChronoUnit.DAYS; +import static java.time.temporal.ChronoUnit.HOURS; +import static java.time.temporal.ChronoUnit.MINUTES; +import static java.time.temporal.ChronoUnit.MICROS; +import static java.time.temporal.ChronoUnit.MILLIS; +import static java.time.temporal.ChronoUnit.HALF_DAYS; import static java.time.temporal.ChronoUnit.NANOS; import static java.time.temporal.ChronoUnit.SECONDS; import static org.testng.Assert.assertEquals; @@ -110,6 +116,7 @@ import java.time.temporal.TemporalAdjuster; import java.time.temporal.TemporalAmount; import java.time.temporal.TemporalField; import java.time.temporal.TemporalQuery; +import java.time.temporal.TemporalUnit; import java.util.ArrayList; import java.util.Arrays; import java.util.List; @@ -1030,6 +1037,56 @@ public class TCKOffsetTime extends AbstractDateTimeTest { assertEquals(test, base); } + //----------------------------------------------------------------------- + // periodUntil(Temporal, TemporalUnit) + //----------------------------------------------------------------------- + @DataProvider(name="periodUntilUnit") + Object[][] data_periodUntilUnit() { + return new Object[][] { + {OffsetTime.of(LocalTime.of(1, 1, 1), ZoneOffset.ofHours(1)), OffsetTime.of(LocalTime.of(13, 1, 1), ZoneOffset.ofHours(1)), HALF_DAYS, 1}, + {OffsetTime.of(LocalTime.of(1, 1, 1), ZoneOffset.ofHours(1)), OffsetTime.of(LocalTime.of(2, 1, 1), ZoneOffset.ofHours(1)), HOURS, 1}, + {OffsetTime.of(LocalTime.of(1, 1, 1), ZoneOffset.ofHours(1)), OffsetTime.of(LocalTime.of(2, 1, 1), ZoneOffset.ofHours(1)), MINUTES, 60}, + {OffsetTime.of(LocalTime.of(1, 1, 1), ZoneOffset.ofHours(1)), OffsetTime.of(LocalTime.of(2, 1, 1), ZoneOffset.ofHours(1)), SECONDS, 3600}, + {OffsetTime.of(LocalTime.of(1, 1, 1), ZoneOffset.ofHours(1)), OffsetTime.of(LocalTime.of(2, 1, 1), ZoneOffset.ofHours(1)), MILLIS, 3600*1000}, + {OffsetTime.of(LocalTime.of(1, 1, 1), ZoneOffset.ofHours(1)), OffsetTime.of(LocalTime.of(2, 1, 1), ZoneOffset.ofHours(1)), MICROS, 3600*1000*1000L}, + {OffsetTime.of(LocalTime.of(1, 1, 1), ZoneOffset.ofHours(1)), OffsetTime.of(LocalTime.of(2, 1, 1), ZoneOffset.ofHours(1)), NANOS, 3600*1000*1000L*1000}, + + {OffsetTime.of(LocalTime.of(1, 1, 1), ZoneOffset.ofHours(1)), OffsetTime.of(LocalTime.of(14, 1, 1), ZoneOffset.ofHours(2)), HALF_DAYS, 1}, + {OffsetTime.of(LocalTime.of(1, 1, 1), ZoneOffset.ofHours(1)), OffsetTime.of(LocalTime.of(3, 1, 1), ZoneOffset.ofHours(2)), HOURS, 1}, + {OffsetTime.of(LocalTime.of(1, 1, 1), ZoneOffset.ofHours(1)), OffsetTime.of(LocalTime.of(3, 1, 1), ZoneOffset.ofHours(2)), MINUTES, 60}, + {OffsetTime.of(LocalTime.of(1, 1, 1), ZoneOffset.ofHours(1)), OffsetTime.of(LocalTime.of(3, 1, 1), ZoneOffset.ofHours(2)), SECONDS, 3600}, + {OffsetTime.of(LocalTime.of(1, 1, 1), ZoneOffset.ofHours(1)), OffsetTime.of(LocalTime.of(3, 1, 1), ZoneOffset.ofHours(2)), MILLIS, 3600*1000}, + {OffsetTime.of(LocalTime.of(1, 1, 1), ZoneOffset.ofHours(1)), OffsetTime.of(LocalTime.of(3, 1, 1), ZoneOffset.ofHours(2)), MICROS, 3600*1000*1000L}, + {OffsetTime.of(LocalTime.of(1, 1, 1), ZoneOffset.ofHours(1)), OffsetTime.of(LocalTime.of(3, 1, 1), ZoneOffset.ofHours(2)), NANOS, 3600*1000*1000L*1000}, + }; + } + + @Test(dataProvider="periodUntilUnit") + public void test_periodUntil_TemporalUnit(OffsetTime offsetTime1, OffsetTime offsetTime2, TemporalUnit unit, long expected) { + long amount = offsetTime1.periodUntil(offsetTime2, unit); + assertEquals(amount, expected); + } + + @Test(dataProvider="periodUntilUnit") + public void test_periodUntil_TemporalUnit_negated(OffsetTime offsetTime1, OffsetTime offsetTime2, TemporalUnit unit, long expected) { + long amount = offsetTime2.periodUntil(offsetTime1, unit); + assertEquals(amount, -expected); + } + + @Test(expectedExceptions=DateTimeException.class) + public void test_periodUntil_InvalidType() { + OffsetTime offsetTime = OffsetTime.of(LocalTime.of(1, 1, 1), ZoneOffset.ofHours(1)); + OffsetDateTime offsetDateTime = offsetTime.atDate(LocalDate.of(1980, 2, 10)); + offsetTime.periodUntil(offsetDateTime, SECONDS); + } + + @Test(expectedExceptions=DateTimeException.class) + public void test_periodUntil_InvalidTemporalUnit() { + OffsetTime offsetTime1 = OffsetTime.of(LocalTime.of(1, 1, 1), ZoneOffset.ofHours(1)); + OffsetTime offsetTime2 = OffsetTime.of(LocalTime.of(2, 1, 1), ZoneOffset.ofHours(1)); + offsetTime1.periodUntil(offsetTime2, MONTHS); + } + //----------------------------------------------------------------------- // format(DateTimeFormatter) //----------------------------------------------------------------------- diff --git a/jdk/test/java/time/tck/java/time/TCKYear.java b/jdk/test/java/time/tck/java/time/TCKYear.java index 570946f3239..ac25f0b4fb3 100644 --- a/jdk/test/java/time/tck/java/time/TCKYear.java +++ b/jdk/test/java/time/tck/java/time/TCKYear.java @@ -71,6 +71,7 @@ import static java.time.temporal.ChronoUnit.MONTHS; import static java.time.temporal.ChronoUnit.WEEKS; import static java.time.temporal.ChronoUnit.YEARS; import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertTrue; import static org.testng.Assert.fail; import java.io.ByteArrayOutputStream; @@ -89,6 +90,7 @@ import java.time.Year; import java.time.YearMonth; import java.time.ZoneId; import java.time.ZoneOffset; +import java.time.chrono.IsoEra; import java.time.chrono.IsoChronology; import java.time.format.DateTimeFormatter; import java.time.format.DateTimeParseException; @@ -525,6 +527,47 @@ public class TCKYear extends AbstractDateTimeTest { Year.of(Year.MIN_VALUE).plusYears(-1000); } + //----------------------------------------------------------------------- + // plus(long, TemporalUnit) + //----------------------------------------------------------------------- + @DataProvider(name="plus_long_TemporalUnit") + Object[][] data_plus_long_TemporalUnit() { + return new Object[][] { + {Year.of(1), 1, ChronoUnit.YEARS, Year.of(2), null}, + {Year.of(1), -12, ChronoUnit.YEARS, Year.of(-11), null}, + {Year.of(1), 0, ChronoUnit.YEARS, Year.of(1), null}, + {Year.of(999999999), 0, ChronoUnit.YEARS, Year.of(999999999), null}, + {Year.of(-999999999), 0, ChronoUnit.YEARS, Year.of(-999999999), null}, + {Year.of(0), -999999999, ChronoUnit.YEARS, Year.of(-999999999), null}, + {Year.of(0), 999999999, ChronoUnit.YEARS, Year.of(999999999), null}, + + {Year.of(-1), 1, ChronoUnit.ERAS, Year.of(2), null}, + {Year.of(5), 1, ChronoUnit.CENTURIES, Year.of(105), null}, + {Year.of(5), 1, ChronoUnit.DECADES, Year.of(15), null}, + + {Year.of(999999999), 1, ChronoUnit.YEARS, null, DateTimeException.class}, + {Year.of(-999999999), -1, ChronoUnit.YEARS, null, DateTimeException.class}, + + {Year.of(1), 0, ChronoUnit.DAYS, null, DateTimeException.class}, + {Year.of(1), 0, ChronoUnit.WEEKS, null, DateTimeException.class}, + {Year.of(1), 0, ChronoUnit.MONTHS, null, DateTimeException.class}, + }; + } + + @Test(groups={"tck"}, dataProvider="plus_long_TemporalUnit") + public void test_plus_long_TemporalUnit(Year base, long amount, TemporalUnit unit, Year expectedYear, Class expectedEx) { + if (expectedEx == null) { + assertEquals(base.plus(amount, unit), expectedYear); + } else { + try { + Year result = base.plus(amount, unit); + fail(); + } catch (Exception ex) { + assertTrue(expectedEx.isInstance(ex)); + } + } + } + //----------------------------------------------------------------------- // minus(Period) //----------------------------------------------------------------------- @@ -616,6 +659,47 @@ public class TCKYear extends AbstractDateTimeTest { Year.of(Year.MIN_VALUE).minusYears(1000); } + //----------------------------------------------------------------------- + // minus(long, TemporalUnit) + //----------------------------------------------------------------------- + @DataProvider(name="minus_long_TemporalUnit") + Object[][] data_minus_long_TemporalUnit() { + return new Object[][] { + {Year.of(1), 1, ChronoUnit.YEARS, Year.of(0), null}, + {Year.of(1), -12, ChronoUnit.YEARS, Year.of(13), null}, + {Year.of(1), 0, ChronoUnit.YEARS, Year.of(1), null}, + {Year.of(999999999), 0, ChronoUnit.YEARS, Year.of(999999999), null}, + {Year.of(-999999999), 0, ChronoUnit.YEARS, Year.of(-999999999), null}, + {Year.of(0), -999999999, ChronoUnit.YEARS, Year.of(999999999), null}, + {Year.of(0), 999999999, ChronoUnit.YEARS, Year.of(-999999999), null}, + + {Year.of(999999999), 1, ChronoUnit.ERAS, Year.of(-999999999 + 1), null}, + {Year.of(105), 1, ChronoUnit.CENTURIES, Year.of(5), null}, + {Year.of(15), 1, ChronoUnit.DECADES, Year.of(5), null}, + + {Year.of(-999999999), 1, ChronoUnit.YEARS, null, DateTimeException.class}, + {Year.of(1), -999999999, ChronoUnit.YEARS, null, DateTimeException.class}, + + {Year.of(1), 0, ChronoUnit.DAYS, null, DateTimeException.class}, + {Year.of(1), 0, ChronoUnit.WEEKS, null, DateTimeException.class}, + {Year.of(1), 0, ChronoUnit.MONTHS, null, DateTimeException.class}, + }; + } + + @Test(groups={"tck"}, dataProvider="minus_long_TemporalUnit") + public void test_minus_long_TemporalUnit(Year base, long amount, TemporalUnit unit, Year expectedYear, Class expectedEx) { + if (expectedEx == null) { + assertEquals(base.minus(amount, unit), expectedYear); + } else { + try { + Year result = base.minus(amount, unit); + fail(); + } catch (Exception ex) { + assertTrue(expectedEx.isInstance(ex)); + } + } + } + //----------------------------------------------------------------------- // adjustInto() //----------------------------------------------------------------------- @@ -640,6 +724,49 @@ public class TCKYear extends AbstractDateTimeTest { test.adjustInto((LocalDate) null); } + //----------------------------------------------------------------------- + // with(TemporalAdjuster) + //----------------------------------------------------------------------- + @Test + public void test_with_TemporalAdjuster() { + Year base = Year.of(-10); + for (int i = -4; i <= 2104; i++) { + Temporal result = base.with(Year.of(i)); + assertEquals(result, Year.of(i)); + } + } + + @Test(expectedExceptions=DateTimeException.class) + public void test_with_BadTemporalAdjuster() { + Year test = Year.of(1); + test.with(LocalTime.of(18, 1, 2)); + } + + //----------------------------------------------------------------------- + // with(TemporalField, long) + //----------------------------------------------------------------------- + @Test(groups={"tck"}) + public void test_with() { + Year base = Year.of(5); + Year result = base.with(ChronoField.ERA, 0); + assertEquals(result, base.with(IsoEra.of(0))); + + int prolepticYear = IsoChronology.INSTANCE.prolepticYear(IsoEra.of(0), 5); + assertEquals(result.get(ChronoField.ERA), 0); + assertEquals(result.get(ChronoField.YEAR), prolepticYear); + assertEquals(result.get(ChronoField.YEAR_OF_ERA), 5); + + result = base.with(ChronoField.YEAR, 10); + assertEquals(result.get(ChronoField.ERA), base.get(ChronoField.ERA)); + assertEquals(result.get(ChronoField.YEAR), 10); + assertEquals(result.get(ChronoField.YEAR_OF_ERA), 10); + + result = base.with(ChronoField.YEAR_OF_ERA, 20); + assertEquals(result.get(ChronoField.ERA), base.get(ChronoField.ERA)); + assertEquals(result.get(ChronoField.YEAR), 20); + assertEquals(result.get(ChronoField.YEAR_OF_ERA), 20); + } + //----------------------------------------------------------------------- // length() //----------------------------------------------------------------------- diff --git a/jdk/test/java/time/tck/java/time/TCKYearMonth.java b/jdk/test/java/time/tck/java/time/TCKYearMonth.java index 8cbf6c472ca..14ccc774b27 100644 --- a/jdk/test/java/time/tck/java/time/TCKYearMonth.java +++ b/jdk/test/java/time/tck/java/time/TCKYearMonth.java @@ -81,6 +81,7 @@ import java.io.DataOutputStream; import java.io.IOException; import java.time.Clock; import java.time.DateTimeException; +import java.time.Duration; import java.time.Instant; import java.time.LocalDate; import java.time.LocalDateTime; @@ -98,6 +99,7 @@ import java.time.temporal.ChronoField; import java.time.temporal.ChronoUnit; import java.time.temporal.JulianFields; import java.time.temporal.TemporalAccessor; +import java.time.temporal.TemporalAmount; import java.time.temporal.TemporalField; import java.time.temporal.TemporalQuery; import java.time.temporal.TemporalUnit; @@ -688,6 +690,106 @@ public class TCKYearMonth extends AbstractDateTimeTest { test.plusMonths(-1); } + //----------------------------------------------------------------------- + // plus(long, TemporalUnit) + //----------------------------------------------------------------------- + @DataProvider(name="plus_long_TemporalUnit") + Object[][] data_plus_long_TemporalUnit() { + return new Object[][] { + {YearMonth.of(1, 10), 1, ChronoUnit.YEARS, YearMonth.of(2, 10), null}, + {YearMonth.of(1, 10), -12, ChronoUnit.YEARS, YearMonth.of(-11, 10), null}, + {YearMonth.of(1, 10), 0, ChronoUnit.YEARS, YearMonth.of(1, 10), null}, + {YearMonth.of(999999999, 12), 0, ChronoUnit.YEARS, YearMonth.of(999999999, 12), null}, + {YearMonth.of(-999999999, 1), 0, ChronoUnit.YEARS, YearMonth.of(-999999999, 1), null}, + {YearMonth.of(0, 1), -999999999, ChronoUnit.YEARS, YearMonth.of(-999999999, 1), null}, + {YearMonth.of(0, 12), 999999999, ChronoUnit.YEARS, YearMonth.of(999999999, 12), null}, + + {YearMonth.of(1, 10), 1, ChronoUnit.MONTHS, YearMonth.of(1, 11), null}, + {YearMonth.of(1, 10), -12, ChronoUnit.MONTHS, YearMonth.of(0, 10), null}, + {YearMonth.of(1, 10), 0, ChronoUnit.MONTHS, YearMonth.of(1, 10), null}, + {YearMonth.of(999999999, 12), 0, ChronoUnit.MONTHS, YearMonth.of(999999999, 12), null}, + {YearMonth.of(-999999999, 1), 0, ChronoUnit.MONTHS, YearMonth.of(-999999999, 1), null}, + {YearMonth.of(-999999999, 2), -1, ChronoUnit.MONTHS, YearMonth.of(-999999999, 1), null}, + {YearMonth.of(999999999, 3), 9, ChronoUnit.MONTHS, YearMonth.of(999999999, 12), null}, + + {YearMonth.of(-1, 10), 1, ChronoUnit.ERAS, YearMonth.of(2, 10), null}, + {YearMonth.of(5, 10), 1, ChronoUnit.CENTURIES, YearMonth.of(105, 10), null}, + {YearMonth.of(5, 10), 1, ChronoUnit.DECADES, YearMonth.of(15, 10), null}, + + {YearMonth.of(999999999, 12), 1, ChronoUnit.MONTHS, null, DateTimeException.class}, + {YearMonth.of(-999999999, 1), -1, ChronoUnit.MONTHS, null, DateTimeException.class}, + + {YearMonth.of(1, 1), 0, ChronoUnit.DAYS, null, DateTimeException.class}, + {YearMonth.of(1, 1), 0, ChronoUnit.WEEKS, null, DateTimeException.class}, + }; + } + + @Test(groups={"tck"}, dataProvider="plus_long_TemporalUnit") + public void test_plus_long_TemporalUnit(YearMonth base, long amount, TemporalUnit unit, YearMonth expectedYearMonth, Class expectedEx) { + if (expectedEx == null) { + assertEquals(base.plus(amount, unit), expectedYearMonth); + } else { + try { + YearMonth result = base.plus(amount, unit); + fail(); + } catch (Exception ex) { + assertTrue(expectedEx.isInstance(ex)); + } + } + } + + //----------------------------------------------------------------------- + // plus(TemporalAmount) + //----------------------------------------------------------------------- + @DataProvider(name="plus_TemporalAmount") + Object[][] data_plus_TemporalAmount() { + return new Object[][] { + {YearMonth.of(1, 1), Period.ofYears(1), YearMonth.of(2, 1), null}, + {YearMonth.of(1, 1), Period.ofYears(-12), YearMonth.of(-11, 1), null}, + {YearMonth.of(1, 1), Period.ofYears(0), YearMonth.of(1, 1), null}, + {YearMonth.of(999999999, 12), Period.ofYears(0), YearMonth.of(999999999, 12), null}, + {YearMonth.of(-999999999, 1), Period.ofYears(0), YearMonth.of(-999999999, 1), null}, + {YearMonth.of(0, 1), Period.ofYears(-999999999), YearMonth.of(-999999999, 1), null}, + {YearMonth.of(0, 12), Period.ofYears(999999999), YearMonth.of(999999999, 12), null}, + + {YearMonth.of(1, 1), Period.ofMonths(1), YearMonth.of(1, 2), null}, + {YearMonth.of(1, 1), Period.ofMonths(-12), YearMonth.of(0, 1), null}, + {YearMonth.of(1, 1), Period.ofMonths(121), YearMonth.of(11, 2), null}, + {YearMonth.of(1, 1), Period.ofMonths(0), YearMonth.of(1, 1), null}, + {YearMonth.of(999999999, 12), Period.ofMonths(0), YearMonth.of(999999999, 12), null}, + {YearMonth.of(-999999999, 1), Period.ofMonths(0), YearMonth.of(-999999999, 1), null}, + {YearMonth.of(-999999999, 2), Period.ofMonths(-1), YearMonth.of(-999999999, 1), null}, + {YearMonth.of(999999999, 11), Period.ofMonths(1), YearMonth.of(999999999, 12), null}, + + {YearMonth.of(1, 1), Period.ofYears(1).withMonths(2), YearMonth.of(2, 3), null}, + {YearMonth.of(1, 1), Period.ofYears(-12).withMonths(-1), YearMonth.of(-12, 12), null}, + + {YearMonth.of(1, 1), Period.ofMonths(2).withYears(1), YearMonth.of(2, 3), null}, + {YearMonth.of(1, 1), Period.ofMonths(-1).withYears(-12), YearMonth.of(-12, 12), null}, + + {YearMonth.of(1, 1), Period.ofDays(365), null, DateTimeException.class}, + {YearMonth.of(1, 1), Duration.ofDays(365), null, DateTimeException.class}, + {YearMonth.of(1, 1), Duration.ofHours(365*24), null, DateTimeException.class}, + {YearMonth.of(1, 1), Duration.ofMinutes(365*24*60), null, DateTimeException.class}, + {YearMonth.of(1, 1), Duration.ofSeconds(365*24*3600), null, DateTimeException.class}, + {YearMonth.of(1, 1), Duration.ofNanos(365*24*3600*1000000000), null, DateTimeException.class}, + }; + } + + @Test(groups={"tck"}, dataProvider="plus_TemporalAmount") + public void test_plus_TemporalAmount(YearMonth base, TemporalAmount temporalAmount, YearMonth expectedYearMonth, Class expectedEx) { + if (expectedEx == null) { + assertEquals(base.plus(temporalAmount), expectedYearMonth); + } else { + try { + YearMonth result = base.plus(temporalAmount); + fail(); + } catch (Exception ex) { + assertTrue(expectedEx.isInstance(ex)); + } + } + } + //----------------------------------------------------------------------- // minusYears() //----------------------------------------------------------------------- @@ -803,6 +905,106 @@ public class TCKYearMonth extends AbstractDateTimeTest { test.minusMonths(1); } + //----------------------------------------------------------------------- + // minus(long, TemporalUnit) + //----------------------------------------------------------------------- + @DataProvider(name="minus_long_TemporalUnit") + Object[][] data_minus_long_TemporalUnit() { + return new Object[][] { + {YearMonth.of(1, 10), 1, ChronoUnit.YEARS, YearMonth.of(0, 10), null}, + {YearMonth.of(1, 10), 12, ChronoUnit.YEARS, YearMonth.of(-11, 10), null}, + {YearMonth.of(1, 10), 0, ChronoUnit.YEARS, YearMonth.of(1, 10), null}, + {YearMonth.of(999999999, 12), 0, ChronoUnit.YEARS, YearMonth.of(999999999, 12), null}, + {YearMonth.of(-999999999, 1), 0, ChronoUnit.YEARS, YearMonth.of(-999999999, 1), null}, + {YearMonth.of(0, 1), 999999999, ChronoUnit.YEARS, YearMonth.of(-999999999, 1), null}, + {YearMonth.of(0, 12), -999999999, ChronoUnit.YEARS, YearMonth.of(999999999, 12), null}, + + {YearMonth.of(1, 10), 1, ChronoUnit.MONTHS, YearMonth.of(1, 9), null}, + {YearMonth.of(1, 10), 12, ChronoUnit.MONTHS, YearMonth.of(0, 10), null}, + {YearMonth.of(1, 10), 0, ChronoUnit.MONTHS, YearMonth.of(1, 10), null}, + {YearMonth.of(999999999, 12), 0, ChronoUnit.MONTHS, YearMonth.of(999999999, 12), null}, + {YearMonth.of(-999999999, 1), 0, ChronoUnit.MONTHS, YearMonth.of(-999999999, 1), null}, + {YearMonth.of(-999999999, 2), 1, ChronoUnit.MONTHS, YearMonth.of(-999999999, 1), null}, + {YearMonth.of(999999999, 11), -1, ChronoUnit.MONTHS, YearMonth.of(999999999, 12), null}, + + {YearMonth.of(1, 10), 1, ChronoUnit.ERAS, YearMonth.of(0, 10), null}, + {YearMonth.of(5, 10), 1, ChronoUnit.CENTURIES, YearMonth.of(-95, 10), null}, + {YearMonth.of(5, 10), 1, ChronoUnit.DECADES, YearMonth.of(-5, 10), null}, + + {YearMonth.of(999999999, 12), -1, ChronoUnit.MONTHS, null, DateTimeException.class}, + {YearMonth.of(-999999999, 1), 1, ChronoUnit.MONTHS, null, DateTimeException.class}, + + {YearMonth.of(1, 1), 0, ChronoUnit.DAYS, null, DateTimeException.class}, + {YearMonth.of(1, 1), 0, ChronoUnit.WEEKS, null, DateTimeException.class}, + }; + } + + @Test(groups={"tck"}, dataProvider="minus_long_TemporalUnit") + public void test_minus_long_TemporalUnit(YearMonth base, long amount, TemporalUnit unit, YearMonth expectedYearMonth, Class expectedEx) { + if (expectedEx == null) { + assertEquals(base.minus(amount, unit), expectedYearMonth); + } else { + try { + YearMonth result = base.minus(amount, unit); + fail(); + } catch (Exception ex) { + assertTrue(expectedEx.isInstance(ex)); + } + } + } + + //----------------------------------------------------------------------- + // minus(TemporalAmount) + //----------------------------------------------------------------------- + @DataProvider(name="minus_TemporalAmount") + Object[][] data_minus_TemporalAmount() { + return new Object[][] { + {YearMonth.of(1, 1), Period.ofYears(1), YearMonth.of(0, 1), null}, + {YearMonth.of(1, 1), Period.ofYears(-12), YearMonth.of(13, 1), null}, + {YearMonth.of(1, 1), Period.ofYears(0), YearMonth.of(1, 1), null}, + {YearMonth.of(999999999, 12), Period.ofYears(0), YearMonth.of(999999999, 12), null}, + {YearMonth.of(-999999999, 1), Period.ofYears(0), YearMonth.of(-999999999, 1), null}, + {YearMonth.of(0, 1), Period.ofYears(999999999), YearMonth.of(-999999999, 1), null}, + {YearMonth.of(0, 12), Period.ofYears(-999999999), YearMonth.of(999999999, 12), null}, + + {YearMonth.of(1, 1), Period.ofMonths(1), YearMonth.of(0, 12), null}, + {YearMonth.of(1, 1), Period.ofMonths(-12), YearMonth.of(2, 1), null}, + {YearMonth.of(1, 1), Period.ofMonths(121), YearMonth.of(-10, 12), null}, + {YearMonth.of(1, 1), Period.ofMonths(0), YearMonth.of(1, 1), null}, + {YearMonth.of(999999999, 12), Period.ofMonths(0), YearMonth.of(999999999, 12), null}, + {YearMonth.of(-999999999, 1), Period.ofMonths(0), YearMonth.of(-999999999, 1), null}, + {YearMonth.of(-999999999, 2), Period.ofMonths(1), YearMonth.of(-999999999, 1), null}, + {YearMonth.of(999999999, 11), Period.ofMonths(-1), YearMonth.of(999999999, 12), null}, + + {YearMonth.of(1, 1), Period.ofYears(1).withMonths(2), YearMonth.of(-1, 11), null}, + {YearMonth.of(1, 1), Period.ofYears(-12).withMonths(-1), YearMonth.of(13, 2), null}, + + {YearMonth.of(1, 1), Period.ofMonths(2).withYears(1), YearMonth.of(-1, 11), null}, + {YearMonth.of(1, 1), Period.ofMonths(-1).withYears(-12), YearMonth.of(13, 2), null}, + + {YearMonth.of(1, 1), Period.ofDays(365), null, DateTimeException.class}, + {YearMonth.of(1, 1), Duration.ofDays(365), null, DateTimeException.class}, + {YearMonth.of(1, 1), Duration.ofHours(365*24), null, DateTimeException.class}, + {YearMonth.of(1, 1), Duration.ofMinutes(365*24*60), null, DateTimeException.class}, + {YearMonth.of(1, 1), Duration.ofSeconds(365*24*3600), null, DateTimeException.class}, + {YearMonth.of(1, 1), Duration.ofNanos(365*24*3600*1000000000), null, DateTimeException.class}, + }; + } + + @Test(groups={"tck"}, dataProvider="minus_TemporalAmount") + public void test_minus_TemporalAmount(YearMonth base, TemporalAmount temporalAmount, YearMonth expectedYearMonth, Class expectedEx) { + if (expectedEx == null) { + assertEquals(base.minus(temporalAmount), expectedYearMonth); + } else { + try { + YearMonth result = base.minus(temporalAmount); + fail(); + } catch (Exception ex) { + assertTrue(expectedEx.isInstance(ex)); + } + } + } + //----------------------------------------------------------------------- // adjustInto() //----------------------------------------------------------------------- diff --git a/jdk/test/java/time/tck/java/time/TCKZoneOffset.java b/jdk/test/java/time/tck/java/time/TCKZoneOffset.java index 18882df474e..94f2eb52e64 100644 --- a/jdk/test/java/time/tck/java/time/TCKZoneOffset.java +++ b/jdk/test/java/time/tck/java/time/TCKZoneOffset.java @@ -73,8 +73,10 @@ import java.time.Instant; import java.time.LocalDate; import java.time.LocalDateTime; import java.time.LocalTime; +import java.time.ZoneId; import java.time.ZoneOffset; import java.time.ZonedDateTime; +import java.time.OffsetDateTime; import java.time.temporal.ChronoField; import java.time.temporal.JulianFields; import java.time.temporal.TemporalAccessor; @@ -619,6 +621,45 @@ public class TCKZoneOffset extends AbstractDateTimeTest { assertEquals(offset2.hashCode() == offset2b.hashCode(), true); } + //----------------------------------------------------------------------- + // adjustInto() + //----------------------------------------------------------------------- + @Test + public void test_adjustInto_ZonedDateTime() { + ZoneOffset base = ZoneOffset.ofHoursMinutesSeconds(1, 1, 1); + for (String zoneId : ZoneId.getAvailableZoneIds()) { + //Do not change offset of ZonedDateTime after adjustInto() + ZonedDateTime zonedDateTime_target = ZonedDateTime.of(LocalDate.of(1909, 2, 2), LocalTime.of(10, 10, 10), ZoneId.of(zoneId)); + ZonedDateTime zonedDateTime_result = (ZonedDateTime)(base.adjustInto(zonedDateTime_target)); + assertEquals(zonedDateTime_target.getOffset(), zonedDateTime_result.getOffset()); + + OffsetDateTime offsetDateTime_target = zonedDateTime_target.toOffsetDateTime(); + OffsetDateTime offsetDateTime_result = (OffsetDateTime)(base.adjustInto(offsetDateTime_target)); + assertEquals(base, offsetDateTime_result.getOffset()); + } + } + + @Test + public void test_adjustInto_OffsetDateTime() { + ZoneOffset base = ZoneOffset.ofHoursMinutesSeconds(1, 1, 1); + for (int i=-18; i<=18; i++) { + OffsetDateTime offsetDateTime_target = OffsetDateTime.of(LocalDate.of(1909, 2, 2), LocalTime.of(10, 10, 10), ZoneOffset.ofHours(i)); + OffsetDateTime offsetDateTime_result = (OffsetDateTime)base.adjustInto(offsetDateTime_target); + assertEquals(base, offsetDateTime_result.getOffset()); + + //Do not change offset of ZonedDateTime after adjustInto() + ZonedDateTime zonedDateTime_target = offsetDateTime_target.toZonedDateTime(); + ZonedDateTime zonedDateTime_result = (ZonedDateTime)(base.adjustInto(zonedDateTime_target)); + assertEquals(zonedDateTime_target.getOffset(), zonedDateTime_result.getOffset()); + } + } + + @Test(expectedExceptions=DateTimeException.class) + public void test_adjustInto_dateOnly() { + ZoneOffset base = ZoneOffset.ofHoursMinutesSeconds(1, 1, 1); + base.adjustInto((LocalDate.of(1909, 2, 2))); + } + //----------------------------------------------------------------------- // toString() //----------------------------------------------------------------------- diff --git a/jdk/test/java/time/tck/java/time/chrono/TCKChronology.java b/jdk/test/java/time/tck/java/time/chrono/TCKChronology.java index 17aadcc1cdb..0077be55315 100644 --- a/jdk/test/java/time/tck/java/time/chrono/TCKChronology.java +++ b/jdk/test/java/time/tck/java/time/chrono/TCKChronology.java @@ -162,39 +162,30 @@ public class TCKChronology { @DataProvider(name = "calendarsystemtype") Object[][] data_CalendarType() { return new Object[][] { - {HijrahChronology.INSTANCE, "islamic", "umalqura"}, - {IsoChronology.INSTANCE, "iso8601", null}, - {JapaneseChronology.INSTANCE, "japanese", null}, - {MinguoChronology.INSTANCE, "roc", null}, - {ThaiBuddhistChronology.INSTANCE, "buddhist", null}, + {HijrahChronology.INSTANCE, "islamic-umalqura"}, + {IsoChronology.INSTANCE, "iso8601"}, + {JapaneseChronology.INSTANCE, "japanese"}, + {MinguoChronology.INSTANCE, "roc"}, + {ThaiBuddhistChronology.INSTANCE, "buddhist"}, }; } @Test(dataProvider = "calendarsystemtype") - public void test_getCalendarType(Chronology chrono, String calendarType, String variant) { + public void test_getCalendarType(Chronology chrono, String calendarType) { String type = calendarType; - if (variant != null) { - type += '-'; - type += variant; - } assertEquals(chrono.getCalendarType(), type); } @Test(dataProvider = "calendarsystemtype") - public void test_lookupLocale(Chronology chrono, String calendarType, String variant) { + public void test_lookupLocale(Chronology chrono, String calendarType) { Locale.Builder builder = new Locale.Builder().setLanguage("en").setRegion("CA"); builder.setUnicodeLocaleKeyword("ca", calendarType); - if (variant != null) { - builder.setUnicodeLocaleKeyword("cv", variant); - } Locale locale = builder.build(); assertEquals(Chronology.ofLocale(locale), chrono); } - /** * Test lookup by calendarType of each chronology. - * The calendarType is split on "-" to separate the calendar and variant. * Verify that the calendar can be found by {@link java.time.chrono.Chronology#ofLocale}. */ @Test @@ -202,15 +193,10 @@ public class TCKChronology { // Test that all available chronologies can be successfully found using ofLocale Set chronos = Chronology.getAvailableChronologies(); for (Chronology chrono : chronos) { - String[] split = chrono.getCalendarType().split("-"); - Locale.Builder builder = new Locale.Builder().setLanguage("en").setRegion("CA"); - builder.setUnicodeLocaleKeyword("ca", split[0]); - if (split.length > 1) { - builder.setUnicodeLocaleKeyword("cv", split[1]); - } + builder.setUnicodeLocaleKeyword("ca", chrono.getCalendarType()); Locale locale = builder.build(); - assertEquals(Chronology.ofLocale(locale), chrono, "Lookup by type and variant"); + assertEquals(Chronology.ofLocale(locale), chrono, "Lookup by type"); } } @@ -218,7 +204,6 @@ public class TCKChronology { public void test_lookupLocale() { Locale.Builder builder = new Locale.Builder().setLanguage("en").setRegion("CA"); builder.setUnicodeLocaleKeyword("ca", "xxx"); - builder.setUnicodeLocaleKeyword("cv", "yyy"); Locale locale = builder.build(); Chronology.ofLocale(locale); diff --git a/jdk/test/java/time/tck/java/time/chrono/TCKChronologySerialization.java b/jdk/test/java/time/tck/java/time/chrono/TCKChronologySerialization.java index c4e44998139..e9d5fb4f1da 100644 --- a/jdk/test/java/time/tck/java/time/chrono/TCKChronologySerialization.java +++ b/jdk/test/java/time/tck/java/time/chrono/TCKChronologySerialization.java @@ -93,7 +93,6 @@ public class TCKChronologySerialization { //----------------------------------------------------------------------- @Test(dataProvider="calendars") public void test_ChronoSerialization(Chronology chrono) throws Exception { - System.out.printf(" ChronoSerialization: %s%n", chrono); ByteArrayOutputStream baos = new ByteArrayOutputStream(); ObjectOutputStream out = new ObjectOutputStream(baos); out.writeObject(chrono); diff --git a/jdk/test/java/time/tck/java/time/chrono/TCKHijrahChronology.java b/jdk/test/java/time/tck/java/time/chrono/TCKHijrahChronology.java index 0efd1ab640a..81b3ab740ae 100644 --- a/jdk/test/java/time/tck/java/time/chrono/TCKHijrahChronology.java +++ b/jdk/test/java/time/tck/java/time/chrono/TCKHijrahChronology.java @@ -62,16 +62,20 @@ import static org.testng.Assert.assertFalse; import static org.testng.Assert.assertTrue; import static org.testng.Assert.fail; +import java.time.Clock; import java.time.DateTimeException; import java.time.LocalDate; import java.time.LocalDateTime; import java.time.Month; import java.time.Period; +import java.time.ZoneId; +import java.time.ZoneOffset; import java.time.chrono.ChronoLocalDate; import java.time.chrono.Chronology; import java.time.chrono.Era; import java.time.chrono.HijrahChronology; import java.time.chrono.HijrahDate; +import java.time.chrono.HijrahEra; import java.time.chrono.IsoChronology; import java.time.chrono.MinguoChronology; import java.time.chrono.MinguoDate; @@ -119,8 +123,11 @@ public class TCKHijrahChronology { //{HijrahChronology.INSTANCE.date(1324, 7, 3), LocalDate.of(1906, 8, 23)}, //{HijrahChronology.INSTANCE.date(1324, 7, 4), LocalDate.of(1906, 8, 24)}, //{HijrahChronology.INSTANCE.date(1325, 1, 1), LocalDate.of(1907, 2, 13)}, - {HijrahChronology.INSTANCE.date(1434, 7, 1), LocalDate.of(2013, 5, 11)}, + {HijrahChronology.INSTANCE.date(1434, 7, 1), LocalDate.of(2013, 5, 11)}, + {HijrahChronology.INSTANCE.date(HijrahEra.AH, 1434, 7, 1), LocalDate.of(2013, 5, 11)}, + {HijrahChronology.INSTANCE.dateYearDay(HijrahEra.AH, 1434, 178), LocalDate.of(2013, 5, 11)}, + {HijrahChronology.INSTANCE.dateYearDay(1434, 178), LocalDate.of(2013, 5, 11)}, //{HijrahChronology.INSTANCE.date(1500, 3, 3), LocalDate.of(2079, 1, 5)}, //{HijrahChronology.INSTANCE.date(1500, 10, 28), LocalDate.of(2079, 8, 25)}, //{HijrahChronology.INSTANCE.date(1500, 10, 29), LocalDate.of(2079, 8, 26)}, @@ -142,6 +149,26 @@ public class TCKHijrahChronology { assertEquals(hijrahDate.get(DAY_OF_WEEK), iso.get(DAY_OF_WEEK), "Hijrah day of week should be same as ISO day of week"); } + @Test + public void test_dateNow(){ + assertEquals(HijrahChronology.INSTANCE.dateNow(), HijrahDate.now()) ; + assertEquals(HijrahChronology.INSTANCE.dateNow(), HijrahDate.now(ZoneId.systemDefault())) ; + assertEquals(HijrahChronology.INSTANCE.dateNow(), HijrahDate.now(Clock.systemDefaultZone())) ; + assertEquals(HijrahChronology.INSTANCE.dateNow(), HijrahDate.now(Clock.systemDefaultZone().getZone())) ; + + assertEquals(HijrahChronology.INSTANCE.dateNow(), HijrahChronology.INSTANCE.dateNow(ZoneId.systemDefault())) ; + assertEquals(HijrahChronology.INSTANCE.dateNow(), HijrahChronology.INSTANCE.dateNow(Clock.systemDefaultZone())) ; + assertEquals(HijrahChronology.INSTANCE.dateNow(), HijrahChronology.INSTANCE.dateNow(Clock.systemDefaultZone().getZone())) ; + + ZoneId zoneId = ZoneId.of("Europe/Paris"); + assertEquals(HijrahChronology.INSTANCE.dateNow(zoneId), HijrahChronology.INSTANCE.dateNow(Clock.system(zoneId))) ; + assertEquals(HijrahChronology.INSTANCE.dateNow(zoneId), HijrahChronology.INSTANCE.dateNow(Clock.system(zoneId).getZone())) ; + assertEquals(HijrahChronology.INSTANCE.dateNow(zoneId), HijrahDate.now(Clock.system(zoneId))) ; + assertEquals(HijrahChronology.INSTANCE.dateNow(zoneId), HijrahDate.now(Clock.system(zoneId).getZone())) ; + + assertEquals(HijrahChronology.INSTANCE.dateNow(ZoneId.of(ZoneOffset.UTC.getId())), HijrahChronology.INSTANCE.dateNow(Clock.systemUTC())) ; + } + @DataProvider(name="badDates") Object[][] data_badDates() { return new Object[][] { diff --git a/jdk/test/java/time/tck/java/time/chrono/TCKJapaneseChronology.java b/jdk/test/java/time/tck/java/time/chrono/TCKJapaneseChronology.java index cd5e859d56a..7802e5bab52 100644 --- a/jdk/test/java/time/tck/java/time/chrono/TCKJapaneseChronology.java +++ b/jdk/test/java/time/tck/java/time/chrono/TCKJapaneseChronology.java @@ -66,12 +66,15 @@ import static org.testng.Assert.assertFalse; import static org.testng.Assert.assertTrue; import static org.testng.Assert.fail; +import java.time.Clock; import java.time.DateTimeException; import java.time.LocalDate; import java.time.LocalDateTime; import java.time.Month; import java.time.Period; import java.time.Year; +import java.time.ZoneId; +import java.time.ZoneOffset; import java.time.chrono.ChronoLocalDate; import java.time.chrono.Chronology; import java.time.chrono.Era; @@ -182,6 +185,16 @@ public class TCKJapaneseChronology { {JapaneseChronology.INSTANCE.dateYearDay(1868, 60), LocalDate.of(1868, 2, 29)}, {JapaneseChronology.INSTANCE.dateYearDay(1928, 60), LocalDate.of(1928, 2, 29)}, {JapaneseChronology.INSTANCE.dateYearDay(1912, 60), LocalDate.of(1912, 2, 29)}, + + {JapaneseDate.ofYearDay(1996, 60), LocalDate.of(1996, 2, 29)}, + {JapaneseDate.ofYearDay(1868, 60), LocalDate.of(1868, 2, 29)}, + {JapaneseDate.ofYearDay(1928, 60), LocalDate.of(1928, 2, 29)}, + {JapaneseDate.ofYearDay(1912, 60), LocalDate.of(1912, 2, 29)}, + + {JapaneseChronology.INSTANCE.dateYearDay(JapaneseEra.HEISEI, 1996 - YDIFF_HEISEI, 60), LocalDate.of(1996, 2, 29)}, + {JapaneseChronology.INSTANCE.dateYearDay(JapaneseEra.MEIJI, 1868 - YDIFF_MEIJI, 60), LocalDate.of(1868, 2, 29)}, + {JapaneseChronology.INSTANCE.dateYearDay(JapaneseEra.SHOWA, 1928 - YDIFF_SHOWA, 60), LocalDate.of(1928, 2, 29)}, +// {JapaneseChronology.INSTANCE.dateYearDay(JapaneseEra.TAISHO, 1916 - YDIFF_TAISHO, 60), LocalDate.of(1912, 2, 29)}, }; } @@ -195,6 +208,26 @@ public class TCKJapaneseChronology { assertEquals(JapaneseChronology.INSTANCE.date(iso), jdate); } + @Test + public void test_dateNow(){ + assertEquals(JapaneseChronology.INSTANCE.dateNow(), JapaneseDate.now()) ; + assertEquals(JapaneseChronology.INSTANCE.dateNow(), JapaneseDate.now(ZoneId.systemDefault())) ; + assertEquals(JapaneseChronology.INSTANCE.dateNow(), JapaneseDate.now(Clock.systemDefaultZone())) ; + assertEquals(JapaneseChronology.INSTANCE.dateNow(), JapaneseDate.now(Clock.systemDefaultZone().getZone())) ; + + assertEquals(JapaneseChronology.INSTANCE.dateNow(), JapaneseChronology.INSTANCE.dateNow(ZoneId.systemDefault())) ; + assertEquals(JapaneseChronology.INSTANCE.dateNow(), JapaneseChronology.INSTANCE.dateNow(Clock.systemDefaultZone())) ; + assertEquals(JapaneseChronology.INSTANCE.dateNow(), JapaneseChronology.INSTANCE.dateNow(Clock.systemDefaultZone().getZone())) ; + + ZoneId zoneId = ZoneId.of("Europe/Paris"); + assertEquals(JapaneseChronology.INSTANCE.dateNow(zoneId), JapaneseChronology.INSTANCE.dateNow(Clock.system(zoneId))) ; + assertEquals(JapaneseChronology.INSTANCE.dateNow(zoneId), JapaneseChronology.INSTANCE.dateNow(Clock.system(zoneId).getZone())) ; + assertEquals(JapaneseChronology.INSTANCE.dateNow(zoneId), JapaneseDate.now(Clock.system(zoneId))) ; + assertEquals(JapaneseChronology.INSTANCE.dateNow(zoneId), JapaneseDate.now(Clock.system(zoneId).getZone())) ; + + assertEquals(JapaneseChronology.INSTANCE.dateNow(ZoneId.of(ZoneOffset.UTC.getId())), JapaneseChronology.INSTANCE.dateNow(Clock.systemUTC())) ; + } + @DataProvider(name="badDates") Object[][] data_badDates() { return new Object[][] { @@ -232,8 +265,6 @@ public class TCKJapaneseChronology { return new Object[][] { {2, JapaneseEra.HEISEI, 1, 1 + YDIFF_HEISEI, false}, {2, JapaneseEra.HEISEI, 100, 100 + YDIFF_HEISEI, true}, - {2, JapaneseEra.HEISEI, 0, YDIFF_HEISEI, true}, - {2, JapaneseEra.HEISEI, -10, -10 + YDIFF_HEISEI, false}, {-1, JapaneseEra.MEIJI, 1, 1 + YDIFF_MEIJI, true}, {-1, JapaneseEra.MEIJI, 4, 4 + YDIFF_MEIJI, false}, diff --git a/jdk/test/java/time/tck/java/time/chrono/TCKThaiBuddhistChronology.java b/jdk/test/java/time/tck/java/time/chrono/TCKThaiBuddhistChronology.java index 04d9dd982ba..f6c79449638 100644 --- a/jdk/test/java/time/tck/java/time/chrono/TCKThaiBuddhistChronology.java +++ b/jdk/test/java/time/tck/java/time/chrono/TCKThaiBuddhistChronology.java @@ -66,12 +66,15 @@ import static org.testng.Assert.assertFalse; import static org.testng.Assert.assertTrue; import static org.testng.Assert.fail; +import java.time.Clock; import java.time.DateTimeException; import java.time.LocalDate; import java.time.LocalDateTime; import java.time.Month; import java.time.Period; import java.time.Year; +import java.time.ZoneId; +import java.time.ZoneOffset; import java.time.chrono.ChronoLocalDate; import java.time.chrono.Chronology; import java.time.chrono.Era; @@ -177,6 +180,15 @@ public class TCKThaiBuddhistChronology { {ThaiBuddhistChronology.INSTANCE.dateYearDay(400 + YDIFF, 60), LocalDate.of(400, 2, 29)}, {ThaiBuddhistChronology.INSTANCE.dateYearDay(2000 + YDIFF, 60), LocalDate.of(2000, 2, 29)}, + {ThaiBuddhistChronology.INSTANCE.dateYearDay(ThaiBuddhistEra.BE, 1916 + YDIFF, 60), LocalDate.of(1916, 2, 29)}, + {ThaiBuddhistChronology.INSTANCE.dateYearDay(ThaiBuddhistEra.BEFORE_BE, -1907 - YDIFF, 60), LocalDate.of(1908, 2, 29)}, + {ThaiBuddhistChronology.INSTANCE.dateYearDay(ThaiBuddhistEra.BE, 2000 + YDIFF, 60), LocalDate.of(2000, 2, 29)}, + {ThaiBuddhistChronology.INSTANCE.dateYearDay(ThaiBuddhistEra.BE, 2400 + YDIFF, 60), LocalDate.of(2400, 2, 29)}, + + {ThaiBuddhistChronology.INSTANCE.date(ThaiBuddhistEra.BE, 1916 + YDIFF, 2, 29 ), LocalDate.of(1916, 2, 29)}, + {ThaiBuddhistChronology.INSTANCE.date(ThaiBuddhistEra.BEFORE_BE, -1907 - YDIFF, 2, 29), LocalDate.of(1908, 2, 29)}, + {ThaiBuddhistChronology.INSTANCE.date(ThaiBuddhistEra.BE, 2000 + YDIFF, 2, 29), LocalDate.of(2000, 2, 29)}, + {ThaiBuddhistChronology.INSTANCE.date(ThaiBuddhistEra.BE, 2400 + YDIFF, 2, 29), LocalDate.of(2400, 2, 29)}, }; } @@ -190,6 +202,26 @@ public class TCKThaiBuddhistChronology { assertEquals(ThaiBuddhistChronology.INSTANCE.date(iso), jdate); } + @Test + public void test_dateNow(){ + assertEquals(ThaiBuddhistChronology.INSTANCE.dateNow(), ThaiBuddhistDate.now()) ; + assertEquals(ThaiBuddhistChronology.INSTANCE.dateNow(), ThaiBuddhistDate.now(ZoneId.systemDefault())) ; + assertEquals(ThaiBuddhistChronology.INSTANCE.dateNow(), ThaiBuddhistDate.now(Clock.systemDefaultZone())) ; + assertEquals(ThaiBuddhistChronology.INSTANCE.dateNow(), ThaiBuddhistDate.now(Clock.systemDefaultZone().getZone())) ; + + assertEquals(ThaiBuddhistChronology.INSTANCE.dateNow(), ThaiBuddhistChronology.INSTANCE.dateNow(ZoneId.systemDefault())) ; + assertEquals(ThaiBuddhistChronology.INSTANCE.dateNow(), ThaiBuddhistChronology.INSTANCE.dateNow(Clock.systemDefaultZone())) ; + assertEquals(ThaiBuddhistChronology.INSTANCE.dateNow(), ThaiBuddhistChronology.INSTANCE.dateNow(Clock.systemDefaultZone().getZone())) ; + + ZoneId zoneId = ZoneId.of("Europe/Paris"); + assertEquals(ThaiBuddhistChronology.INSTANCE.dateNow(zoneId), ThaiBuddhistChronology.INSTANCE.dateNow(Clock.system(zoneId))) ; + assertEquals(ThaiBuddhistChronology.INSTANCE.dateNow(zoneId), ThaiBuddhistChronology.INSTANCE.dateNow(Clock.system(zoneId).getZone())) ; + assertEquals(ThaiBuddhistChronology.INSTANCE.dateNow(zoneId), ThaiBuddhistDate.now(Clock.system(zoneId))) ; + assertEquals(ThaiBuddhistChronology.INSTANCE.dateNow(zoneId), ThaiBuddhistDate.now(Clock.system(zoneId).getZone())) ; + + assertEquals(ThaiBuddhistChronology.INSTANCE.dateNow(ZoneId.of(ZoneOffset.UTC.getId())), ThaiBuddhistChronology.INSTANCE.dateNow(Clock.systemUTC())) ; + } + @DataProvider(name="badDates") Object[][] data_badDates() { return new Object[][] { diff --git a/jdk/test/java/time/tck/java/time/format/TCKDateTimeFormatter.java b/jdk/test/java/time/tck/java/time/format/TCKDateTimeFormatter.java index e8f137f963d..8e2091e9888 100644 --- a/jdk/test/java/time/tck/java/time/format/TCKDateTimeFormatter.java +++ b/jdk/test/java/time/tck/java/time/format/TCKDateTimeFormatter.java @@ -90,7 +90,7 @@ import java.time.chrono.Chronology; import java.time.chrono.IsoChronology; import java.time.chrono.ThaiBuddhistChronology; import java.time.chrono.ThaiBuddhistDate; -import java.time.format.DateTimeFormatSymbols; +import java.time.format.DecimalStyle; import java.time.format.DateTimeFormatter; import java.time.format.DateTimeFormatterBuilder; import java.time.format.DateTimeParseException; @@ -134,14 +134,14 @@ public class TCKDateTimeFormatter { //----------------------------------------------------------------------- @Test public void test_withLocale() { - DateTimeFormatter base = fmt.withLocale(Locale.ENGLISH).withSymbols(DateTimeFormatSymbols.STANDARD); + DateTimeFormatter base = fmt.withLocale(Locale.ENGLISH).withDecimalStyle(DecimalStyle.STANDARD); DateTimeFormatter test = base.withLocale(Locale.GERMAN); assertEquals(test.getLocale(), Locale.GERMAN); } @Test(expectedExceptions=NullPointerException.class) public void test_withLocale_null() { - DateTimeFormatter base = fmt.withLocale(Locale.ENGLISH).withSymbols(DateTimeFormatSymbols.STANDARD); + DateTimeFormatter base = fmt.withLocale(Locale.ENGLISH).withDecimalStyle(DecimalStyle.STANDARD); base.withLocale((Locale) null); } @@ -192,7 +192,9 @@ public class TCKDateTimeFormatter { .appendValue(YEAR).appendLiteral('-').appendValue(MONTH_OF_YEAR).appendLiteral('-') .appendValue(DAY_OF_MONTH).appendLiteral('-').appendValue(DAY_OF_YEAR).toFormatter(); DateTimeFormatter f = base.withResolverFields(YEAR, DAY_OF_YEAR); - assertEquals(f.getResolverFields(), new HashSet<>(Arrays.asList(YEAR, DAY_OF_YEAR))); + Set expected = new HashSet<>(Arrays.asList(YEAR, DAY_OF_YEAR)); + // Use set.equals(); testNG comparison of Collections is ordered + assertTrue(f.getResolverFields().equals(expected), "ResolveFields: " + f.getResolverFields()); try { base.parse("2012-6-30-321", LocalDate::from); // wrong month/day-of-month fail(); @@ -392,27 +394,27 @@ public class TCKDateTimeFormatter { //----------------------------------------------------------------------- @Test public void test_format_TemporalAccessor_simple() { - DateTimeFormatter test = fmt.withLocale(Locale.ENGLISH).withSymbols(DateTimeFormatSymbols.STANDARD); + DateTimeFormatter test = fmt.withLocale(Locale.ENGLISH).withDecimalStyle(DecimalStyle.STANDARD); String result = test.format(LocalDate.of(2008, 6, 30)); assertEquals(result, "ONE30"); } @Test(expectedExceptions = DateTimeException.class) public void test_format_TemporalAccessor_noSuchField() { - DateTimeFormatter test = fmt.withLocale(Locale.ENGLISH).withSymbols(DateTimeFormatSymbols.STANDARD); + DateTimeFormatter test = fmt.withLocale(Locale.ENGLISH).withDecimalStyle(DecimalStyle.STANDARD); test.format(LocalTime.of(11, 30)); } @Test(expectedExceptions = NullPointerException.class) public void test_format_TemporalAccessor_null() { - DateTimeFormatter test = fmt.withLocale(Locale.ENGLISH).withSymbols(DateTimeFormatSymbols.STANDARD); + DateTimeFormatter test = fmt.withLocale(Locale.ENGLISH).withDecimalStyle(DecimalStyle.STANDARD); test.format((TemporalAccessor) null); } //----------------------------------------------------------------------- @Test public void test_print_TemporalAppendable() throws Exception { - DateTimeFormatter test = fmt.withLocale(Locale.ENGLISH).withSymbols(DateTimeFormatSymbols.STANDARD); + DateTimeFormatter test = fmt.withLocale(Locale.ENGLISH).withDecimalStyle(DecimalStyle.STANDARD); StringBuilder buf = new StringBuilder(); test.formatTo(LocalDate.of(2008, 6, 30), buf); assertEquals(buf.toString(), "ONE30"); @@ -420,21 +422,21 @@ public class TCKDateTimeFormatter { @Test(expectedExceptions=DateTimeException.class) public void test_print_TemporalAppendable_noSuchField() throws Exception { - DateTimeFormatter test = fmt.withLocale(Locale.ENGLISH).withSymbols(DateTimeFormatSymbols.STANDARD); + DateTimeFormatter test = fmt.withLocale(Locale.ENGLISH).withDecimalStyle(DecimalStyle.STANDARD); StringBuilder buf = new StringBuilder(); test.formatTo(LocalTime.of(11, 30), buf); } @Test(expectedExceptions=NullPointerException.class) public void test_print_TemporalAppendable_nullTemporal() throws Exception { - DateTimeFormatter test = fmt.withLocale(Locale.ENGLISH).withSymbols(DateTimeFormatSymbols.STANDARD); + DateTimeFormatter test = fmt.withLocale(Locale.ENGLISH).withDecimalStyle(DecimalStyle.STANDARD); StringBuilder buf = new StringBuilder(); test.formatTo((TemporalAccessor) null, buf); } @Test(expectedExceptions=NullPointerException.class) public void test_print_TemporalAppendable_nullAppendable() throws Exception { - DateTimeFormatter test = fmt.withLocale(Locale.ENGLISH).withSymbols(DateTimeFormatSymbols.STANDARD); + DateTimeFormatter test = fmt.withLocale(Locale.ENGLISH).withDecimalStyle(DecimalStyle.STANDARD); test.formatTo(LocalDate.of(2008, 6, 30), (Appendable) null); } @@ -443,7 +445,7 @@ public class TCKDateTimeFormatter { //----------------------------------------------------------------------- @Test public void test_parse_CharSequence() { - DateTimeFormatter test = fmt.withLocale(Locale.ENGLISH).withSymbols(DateTimeFormatSymbols.STANDARD); + DateTimeFormatter test = fmt.withLocale(Locale.ENGLISH).withDecimalStyle(DecimalStyle.STANDARD); TemporalAccessor result = test.parse("ONE30"); assertEquals(result.isSupported(DAY_OF_MONTH), true); assertEquals(result.getLong(DAY_OF_MONTH), 30L); @@ -466,7 +468,7 @@ public class TCKDateTimeFormatter { @Test(expectedExceptions=NullPointerException.class) public void test_parse_CharSequence_null() { - DateTimeFormatter test = fmt.withLocale(Locale.ENGLISH).withSymbols(DateTimeFormatSymbols.STANDARD); + DateTimeFormatter test = fmt.withLocale(Locale.ENGLISH).withDecimalStyle(DecimalStyle.STANDARD); test.parse((String) null); } @@ -475,7 +477,7 @@ public class TCKDateTimeFormatter { //----------------------------------------------------------------------- @Test public void test_parse_CharSequence_ParsePosition() { - DateTimeFormatter test = fmt.withLocale(Locale.ENGLISH).withSymbols(DateTimeFormatSymbols.STANDARD); + DateTimeFormatter test = fmt.withLocale(Locale.ENGLISH).withDecimalStyle(DecimalStyle.STANDARD); ParsePosition pos = new ParsePosition(3); TemporalAccessor result = test.parse("XXXONE30XXX", pos); assertEquals(pos.getIndex(), 8); @@ -523,13 +525,13 @@ public class TCKDateTimeFormatter { @Test(expectedExceptions=NullPointerException.class) public void test_parse_CharSequence_ParsePosition_nullText() { - DateTimeFormatter test = fmt.withLocale(Locale.ENGLISH).withSymbols(DateTimeFormatSymbols.STANDARD); + DateTimeFormatter test = fmt.withLocale(Locale.ENGLISH).withDecimalStyle(DecimalStyle.STANDARD); test.parse((CharSequence) null, new ParsePosition(0)); } @Test(expectedExceptions=NullPointerException.class) public void test_parse_CharSequence_ParsePosition_nullParsePosition() { - DateTimeFormatter test = fmt.withLocale(Locale.ENGLISH).withSymbols(DateTimeFormatSymbols.STANDARD); + DateTimeFormatter test = fmt.withLocale(Locale.ENGLISH).withDecimalStyle(DecimalStyle.STANDARD); test.parse("Text", (ParsePosition) null); } @@ -594,7 +596,7 @@ public class TCKDateTimeFormatter { @Test(expectedExceptions=NullPointerException.class) public void test_parse_Query_String_nullRule() throws Exception { - DateTimeFormatter test = fmt.withLocale(Locale.ENGLISH).withSymbols(DateTimeFormatSymbols.STANDARD); + DateTimeFormatter test = fmt.withLocale(Locale.ENGLISH).withDecimalStyle(DecimalStyle.STANDARD); test.parse("30", (TemporalQuery) null); } @@ -630,7 +632,7 @@ public class TCKDateTimeFormatter { @Test(expectedExceptions=DateTimeParseException.class) public void test_parseBest_String_parseErrorLongText() throws Exception { - DateTimeFormatter test = fmt.withLocale(Locale.ENGLISH).withSymbols(DateTimeFormatSymbols.STANDARD); + DateTimeFormatter test = fmt.withLocale(Locale.ENGLISH).withDecimalStyle(DecimalStyle.STANDARD); try { test.parseBest("ONEXXX67890123456789012345678901234567890123456789012345678901234567890123456789", ZonedDateTime::from, LocalDate::from); } catch (DateTimeParseException ex) { @@ -644,7 +646,7 @@ public class TCKDateTimeFormatter { @Test(expectedExceptions=DateTimeParseException.class) public void test_parseBest_String_parseIncomplete() throws Exception { - DateTimeFormatter test = fmt.withLocale(Locale.ENGLISH).withSymbols(DateTimeFormatSymbols.STANDARD); + DateTimeFormatter test = fmt.withLocale(Locale.ENGLISH).withDecimalStyle(DecimalStyle.STANDARD); try { test.parseBest("ONE30SomethingElse", ZonedDateTime::from, LocalDate::from); } catch (DateTimeParseException ex) { @@ -658,32 +660,32 @@ public class TCKDateTimeFormatter { @Test(expectedExceptions=NullPointerException.class) public void test_parseBest_String_nullText() throws Exception { - DateTimeFormatter test = fmt.withLocale(Locale.ENGLISH).withSymbols(DateTimeFormatSymbols.STANDARD); + DateTimeFormatter test = fmt.withLocale(Locale.ENGLISH).withDecimalStyle(DecimalStyle.STANDARD); test.parseBest((String) null, ZonedDateTime::from, LocalDate::from); } @Test(expectedExceptions=NullPointerException.class) public void test_parseBest_String_nullRules() throws Exception { - DateTimeFormatter test = fmt.withLocale(Locale.ENGLISH).withSymbols(DateTimeFormatSymbols.STANDARD); + DateTimeFormatter test = fmt.withLocale(Locale.ENGLISH).withDecimalStyle(DecimalStyle.STANDARD); test.parseBest("30", (TemporalQuery[]) null); } @Test(expectedExceptions=IllegalArgumentException.class) public void test_parseBest_String_zeroRules() throws Exception { - DateTimeFormatter test = fmt.withLocale(Locale.ENGLISH).withSymbols(DateTimeFormatSymbols.STANDARD); + DateTimeFormatter test = fmt.withLocale(Locale.ENGLISH).withDecimalStyle(DecimalStyle.STANDARD); test.parseBest("30", new TemporalQuery[0]); } @Test(expectedExceptions=IllegalArgumentException.class) public void test_parseBest_String_oneRule() throws Exception { - DateTimeFormatter test = fmt.withLocale(Locale.ENGLISH).withSymbols(DateTimeFormatSymbols.STANDARD); + DateTimeFormatter test = fmt.withLocale(Locale.ENGLISH).withDecimalStyle(DecimalStyle.STANDARD); test.parseBest("30", LocalDate::from); } //----------------------------------------------------------------------- @Test public void test_parseUnresolved_StringParsePosition() { - DateTimeFormatter test = fmt.withLocale(Locale.ENGLISH).withSymbols(DateTimeFormatSymbols.STANDARD); + DateTimeFormatter test = fmt.withLocale(Locale.ENGLISH).withDecimalStyle(DecimalStyle.STANDARD); ParsePosition pos = new ParsePosition(0); TemporalAccessor result = test.parseUnresolved("ONE30XXX", pos); assertEquals(pos.getIndex(), 5); @@ -693,7 +695,7 @@ public class TCKDateTimeFormatter { @Test public void test_parseUnresolved_StringParsePosition_parseError() { - DateTimeFormatter test = fmt.withLocale(Locale.ENGLISH).withSymbols(DateTimeFormatSymbols.STANDARD); + DateTimeFormatter test = fmt.withLocale(Locale.ENGLISH).withDecimalStyle(DecimalStyle.STANDARD); ParsePosition pos = new ParsePosition(0); TemporalAccessor result = test.parseUnresolved("ONEXXX", pos); assertEquals(pos.getIndex(), 0); @@ -725,20 +727,20 @@ public class TCKDateTimeFormatter { @Test(expectedExceptions=NullPointerException.class) public void test_parseUnresolved_StringParsePosition_nullString() { - DateTimeFormatter test = fmt.withLocale(Locale.ENGLISH).withSymbols(DateTimeFormatSymbols.STANDARD); + DateTimeFormatter test = fmt.withLocale(Locale.ENGLISH).withDecimalStyle(DecimalStyle.STANDARD); ParsePosition pos = new ParsePosition(0); test.parseUnresolved((String) null, pos); } @Test(expectedExceptions=NullPointerException.class) public void test_parseUnresolved_StringParsePosition_nullParsePosition() { - DateTimeFormatter test = fmt.withLocale(Locale.ENGLISH).withSymbols(DateTimeFormatSymbols.STANDARD); + DateTimeFormatter test = fmt.withLocale(Locale.ENGLISH).withDecimalStyle(DecimalStyle.STANDARD); test.parseUnresolved("ONE30", (ParsePosition) null); } @Test(expectedExceptions=IndexOutOfBoundsException.class) public void test_parseUnresolved_StringParsePosition_invalidPosition() { - DateTimeFormatter test = fmt.withLocale(Locale.ENGLISH).withSymbols(DateTimeFormatSymbols.STANDARD); + DateTimeFormatter test = fmt.withLocale(Locale.ENGLISH).withDecimalStyle(DecimalStyle.STANDARD); ParsePosition pos = new ParsePosition(6); test.parseUnresolved("ONE30", pos); } @@ -747,7 +749,7 @@ public class TCKDateTimeFormatter { //----------------------------------------------------------------------- @Test public void test_toFormat_format() throws Exception { - DateTimeFormatter test = fmt.withLocale(Locale.ENGLISH).withSymbols(DateTimeFormatSymbols.STANDARD); + DateTimeFormatter test = fmt.withLocale(Locale.ENGLISH).withDecimalStyle(DecimalStyle.STANDARD); Format format = test.toFormat(); String result = format.format(LocalDate.of(2008, 6, 30)); assertEquals(result, "ONE30"); @@ -755,14 +757,14 @@ public class TCKDateTimeFormatter { @Test(expectedExceptions=NullPointerException.class) public void test_toFormat_format_null() throws Exception { - DateTimeFormatter test = fmt.withLocale(Locale.ENGLISH).withSymbols(DateTimeFormatSymbols.STANDARD); + DateTimeFormatter test = fmt.withLocale(Locale.ENGLISH).withDecimalStyle(DecimalStyle.STANDARD); Format format = test.toFormat(); format.format(null); } @Test(expectedExceptions=IllegalArgumentException.class) public void test_toFormat_format_notTemporal() throws Exception { - DateTimeFormatter test = fmt.withLocale(Locale.ENGLISH).withSymbols(DateTimeFormatSymbols.STANDARD); + DateTimeFormatter test = fmt.withLocale(Locale.ENGLISH).withDecimalStyle(DecimalStyle.STANDARD); Format format = test.toFormat(); format.format("Not a Temporal"); } @@ -770,7 +772,7 @@ public class TCKDateTimeFormatter { //----------------------------------------------------------------------- @Test public void test_toFormat_parseObject_String() throws Exception { - DateTimeFormatter test = fmt.withLocale(Locale.ENGLISH).withSymbols(DateTimeFormatSymbols.STANDARD); + DateTimeFormatter test = fmt.withLocale(Locale.ENGLISH).withDecimalStyle(DecimalStyle.STANDARD); Format format = test.toFormat(); TemporalAccessor result = (TemporalAccessor) format.parseObject("ONE30"); assertEquals(result.isSupported(DAY_OF_MONTH), true); @@ -779,7 +781,7 @@ public class TCKDateTimeFormatter { @Test(expectedExceptions=ParseException.class) public void test_toFormat_parseObject_String_parseError() throws Exception { - DateTimeFormatter test = fmt.withLocale(Locale.ENGLISH).withSymbols(DateTimeFormatSymbols.STANDARD); + DateTimeFormatter test = fmt.withLocale(Locale.ENGLISH).withDecimalStyle(DecimalStyle.STANDARD); Format format = test.toFormat(); try { format.parseObject("ONEXXX"); @@ -792,7 +794,7 @@ public class TCKDateTimeFormatter { @Test(expectedExceptions=ParseException.class) public void test_toFormat_parseObject_String_parseErrorLongText() throws Exception { - DateTimeFormatter test = fmt.withLocale(Locale.ENGLISH).withSymbols(DateTimeFormatSymbols.STANDARD); + DateTimeFormatter test = fmt.withLocale(Locale.ENGLISH).withDecimalStyle(DecimalStyle.STANDARD); Format format = test.toFormat(); try { format.parseObject("ONEXXX67890123456789012345678901234567890123456789012345678901234567890123456789"); @@ -806,7 +808,7 @@ public class TCKDateTimeFormatter { @Test(expectedExceptions=NullPointerException.class) public void test_toFormat_parseObject_String_null() throws Exception { - DateTimeFormatter test = fmt.withLocale(Locale.ENGLISH).withSymbols(DateTimeFormatSymbols.STANDARD); + DateTimeFormatter test = fmt.withLocale(Locale.ENGLISH).withDecimalStyle(DecimalStyle.STANDARD); Format format = test.toFormat(); format.parseObject((String) null); } @@ -814,7 +816,7 @@ public class TCKDateTimeFormatter { //----------------------------------------------------------------------- @Test public void test_toFormat_parseObject_StringParsePosition() throws Exception { - DateTimeFormatter test = fmt.withLocale(Locale.ENGLISH).withSymbols(DateTimeFormatSymbols.STANDARD); + DateTimeFormatter test = fmt.withLocale(Locale.ENGLISH).withDecimalStyle(DecimalStyle.STANDARD); Format format = test.toFormat(); ParsePosition pos = new ParsePosition(0); TemporalAccessor result = (TemporalAccessor) format.parseObject("ONE30XXX", pos); @@ -826,7 +828,7 @@ public class TCKDateTimeFormatter { @Test public void test_toFormat_parseObject_StringParsePosition_parseError() throws Exception { - DateTimeFormatter test = fmt.withLocale(Locale.ENGLISH).withSymbols(DateTimeFormatSymbols.STANDARD); + DateTimeFormatter test = fmt.withLocale(Locale.ENGLISH).withDecimalStyle(DecimalStyle.STANDARD); Format format = test.toFormat(); ParsePosition pos = new ParsePosition(0); TemporalAccessor result = (TemporalAccessor) format.parseObject("ONEXXX", pos); @@ -838,7 +840,7 @@ public class TCKDateTimeFormatter { @Test(expectedExceptions=NullPointerException.class) public void test_toFormat_parseObject_StringParsePosition_nullString() throws Exception { // SimpleDateFormat has this behavior - DateTimeFormatter test = fmt.withLocale(Locale.ENGLISH).withSymbols(DateTimeFormatSymbols.STANDARD); + DateTimeFormatter test = fmt.withLocale(Locale.ENGLISH).withDecimalStyle(DecimalStyle.STANDARD); Format format = test.toFormat(); ParsePosition pos = new ParsePosition(0); format.parseObject((String) null, pos); @@ -847,7 +849,7 @@ public class TCKDateTimeFormatter { @Test(expectedExceptions=NullPointerException.class) public void test_toFormat_parseObject_StringParsePosition_nullParsePosition() throws Exception { // SimpleDateFormat has this behavior - DateTimeFormatter test = fmt.withLocale(Locale.ENGLISH).withSymbols(DateTimeFormatSymbols.STANDARD); + DateTimeFormatter test = fmt.withLocale(Locale.ENGLISH).withDecimalStyle(DecimalStyle.STANDARD); Format format = test.toFormat(); format.parseObject("ONE30", (ParsePosition) null); } @@ -855,7 +857,7 @@ public class TCKDateTimeFormatter { @Test public void test_toFormat_parseObject_StringParsePosition_invalidPosition_tooBig() throws Exception { // SimpleDateFormat has this behavior - DateTimeFormatter dtf = fmt.withLocale(Locale.ENGLISH).withSymbols(DateTimeFormatSymbols.STANDARD); + DateTimeFormatter dtf = fmt.withLocale(Locale.ENGLISH).withDecimalStyle(DecimalStyle.STANDARD); ParsePosition pos = new ParsePosition(6); Format test = dtf.toFormat(); assertNull(test.parseObject("ONE30", pos)); @@ -865,7 +867,7 @@ public class TCKDateTimeFormatter { @Test public void test_toFormat_parseObject_StringParsePosition_invalidPosition_tooSmall() throws Exception { // SimpleDateFormat throws StringIndexOutOfBoundException - DateTimeFormatter dtf = fmt.withLocale(Locale.ENGLISH).withSymbols(DateTimeFormatSymbols.STANDARD); + DateTimeFormatter dtf = fmt.withLocale(Locale.ENGLISH).withDecimalStyle(DecimalStyle.STANDARD); ParsePosition pos = new ParsePosition(-1); Format test = dtf.toFormat(); assertNull(test.parseObject("ONE30", pos)); diff --git a/jdk/test/java/time/tck/java/time/format/TCKDateTimeFormatters.java b/jdk/test/java/time/tck/java/time/format/TCKDateTimeFormatters.java index 3ba962ad212..f30eb9906b3 100644 --- a/jdk/test/java/time/tck/java/time/format/TCKDateTimeFormatters.java +++ b/jdk/test/java/time/tck/java/time/format/TCKDateTimeFormatters.java @@ -63,6 +63,7 @@ import static java.time.temporal.ChronoField.DAY_OF_MONTH; import static java.time.temporal.ChronoField.DAY_OF_WEEK; import static java.time.temporal.ChronoField.DAY_OF_YEAR; import static java.time.temporal.ChronoField.HOUR_OF_DAY; +import static java.time.temporal.ChronoField.INSTANT_SECONDS; import static java.time.temporal.ChronoField.MINUTE_OF_HOUR; import static java.time.temporal.ChronoField.MONTH_OF_YEAR; import static java.time.temporal.ChronoField.NANO_OF_SECOND; @@ -1180,6 +1181,52 @@ public class TCKDateTimeFormatters { //----------------------------------------------------------------------- //----------------------------------------------------------------------- //----------------------------------------------------------------------- + @DataProvider(name="sample_isoInstant") + Object[][] provider_sample_isoInstant() { + return new Object[][]{ + {0, 0, "1970-01-01T00:00:00Z", null}, + {0, null, "1970-01-01T00:00:00Z", null}, + {0, -1, null, DateTimeException.class}, + + {-1, 0, "1969-12-31T23:59:59Z", null}, + {1, 0, "1970-01-01T00:00:01Z", null}, + {60, 0, "1970-01-01T00:01:00Z", null}, + {3600, 0, "1970-01-01T01:00:00Z", null}, + {86400, 0, "1970-01-02T00:00:00Z", null}, + + {0, 1, "1970-01-01T00:00:00.000000001Z", null}, + {0, 2, "1970-01-01T00:00:00.000000002Z", null}, + {0, 10, "1970-01-01T00:00:00.000000010Z", null}, + {0, 100, "1970-01-01T00:00:00.000000100Z", null}, + }; + } + + @Test(dataProvider="sample_isoInstant") + public void test_print_isoInstant( + long instantSecs, Integer nano, String expected, Class expectedEx) { + TemporalAccessor test = buildAccessorInstant(instantSecs, nano); + if (expectedEx == null) { + assertEquals(DateTimeFormatter.ISO_INSTANT.format(test), expected); + } else { + try { + DateTimeFormatter.ISO_INSTANT.format(test); + fail(); + } catch (Exception ex) { + assertTrue(expectedEx.isInstance(ex)); + } + } + } + + @Test(dataProvider="sample_isoInstant") + public void test_parse_isoInstant( + long instantSecs, Integer nano, String input, Class invalid) { + if (input != null) { + TemporalAccessor parsed = DateTimeFormatter.ISO_INSTANT.parseUnresolved(input, new ParsePosition(0)); + assertEquals(parsed.getLong(INSTANT_SECONDS), instantSecs); + assertEquals(parsed.getLong(NANO_OF_SECOND), (nano == null ? 0 : nano)); + } + } + @Test public void test_isoInstant_basics() { assertEquals(DateTimeFormatter.ISO_INSTANT.getChronology(), null); @@ -1334,6 +1381,15 @@ public class TCKDateTimeFormatters { return mock; } + private TemporalAccessor buildAccessorInstant(long instantSecs, Integer nano) { + MockAccessor mock = new MockAccessor(); + mock.fields.put(INSTANT_SECONDS, instantSecs); + if (nano != null) { + mock.fields.put(NANO_OF_SECOND, (long) nano); + } + return mock; + } + private void buildCalendrical(Expected expected, String offsetId, String zoneId) { if (offsetId != null) { expected.add(ZoneOffset.of(offsetId)); diff --git a/jdk/test/java/time/tck/java/time/format/TCKDateTimeParseResolver.java b/jdk/test/java/time/tck/java/time/format/TCKDateTimeParseResolver.java index 8b6c1340688..9b249f19224 100644 --- a/jdk/test/java/time/tck/java/time/format/TCKDateTimeParseResolver.java +++ b/jdk/test/java/time/tck/java/time/format/TCKDateTimeParseResolver.java @@ -59,6 +59,9 @@ */ package tck.java.time.format; +import static java.time.format.ResolverStyle.LENIENT; +import static java.time.format.ResolverStyle.SMART; +import static java.time.format.ResolverStyle.STRICT; import static java.time.temporal.ChronoField.ALIGNED_DAY_OF_WEEK_IN_MONTH; import static java.time.temporal.ChronoField.ALIGNED_DAY_OF_WEEK_IN_YEAR; import static java.time.temporal.ChronoField.ALIGNED_WEEK_OF_MONTH; @@ -88,11 +91,15 @@ import static java.time.temporal.ChronoField.SECOND_OF_MINUTE; import static java.time.temporal.ChronoField.YEAR; import static java.time.temporal.ChronoField.YEAR_OF_ERA; import static org.testng.Assert.assertEquals; +import static org.testng.Assert.fail; import java.time.LocalDate; import java.time.LocalTime; +import java.time.Period; import java.time.format.DateTimeFormatter; import java.time.format.DateTimeFormatterBuilder; +import java.time.format.DateTimeParseException; +import java.time.format.ResolverStyle; import java.time.temporal.IsoFields; import java.time.temporal.TemporalAccessor; import java.time.temporal.TemporalField; @@ -519,4 +526,350 @@ public class TCKDateTimeParseResolver { assertEquals(accessor.query(TemporalQuery.localTime()), null); } + //----------------------------------------------------------------------- + @DataProvider(name="resolveFourToTime") + Object[][] data_resolveFourToTime() { + return new Object[][]{ + // merge + {null, 0, 0, 0, 0, LocalTime.of(0, 0, 0, 0), Period.ZERO}, + {null, 1, 0, 0, 0, LocalTime.of(1, 0, 0, 0), Period.ZERO}, + {null, 0, 2, 0, 0, LocalTime.of(0, 2, 0, 0), Period.ZERO}, + {null, 0, 0, 3, 0, LocalTime.of(0, 0, 3, 0), Period.ZERO}, + {null, 0, 0, 0, 4, LocalTime.of(0, 0, 0, 4), Period.ZERO}, + {null, 1, 2, 3, 4, LocalTime.of(1, 2, 3, 4), Period.ZERO}, + {null, 23, 59, 59, 123456789, LocalTime.of(23, 59, 59, 123456789), Period.ZERO}, + + {ResolverStyle.STRICT, 14, 59, 60, 123456789, null, null}, + {ResolverStyle.SMART, 14, 59, 60, 123456789, null, null}, + {ResolverStyle.LENIENT, 14, 59, 60, 123456789, LocalTime.of(15, 0, 0, 123456789), Period.ZERO}, + + {ResolverStyle.STRICT, 23, 59, 60, 123456789, null, null}, + {ResolverStyle.SMART, 23, 59, 60, 123456789, null, null}, + {ResolverStyle.LENIENT, 23, 59, 60, 123456789, LocalTime.of(0, 0, 0, 123456789), Period.ofDays(1)}, + + {ResolverStyle.STRICT, 24, 0, 0, 0, null, null}, + {ResolverStyle.SMART, 24, 0, 0, 0, LocalTime.of(0, 0, 0, 0), Period.ofDays(1)}, + {ResolverStyle.LENIENT, 24, 0, 0, 0, LocalTime.of(0, 0, 0, 0), Period.ofDays(1)}, + + {ResolverStyle.STRICT, 24, 1, 0, 0, null, null}, + {ResolverStyle.SMART, 24, 1, 0, 0, null, null}, + {ResolverStyle.LENIENT, 24, 1, 0, 0, LocalTime.of(0, 1, 0, 0), Period.ofDays(1)}, + + {ResolverStyle.STRICT, 25, 0, 0, 0, null, null}, + {ResolverStyle.SMART, 25, 0, 0, 0, null, null}, + {ResolverStyle.LENIENT, 25, 0, 0, 0, LocalTime.of(1, 0, 0, 0), Period.ofDays(1)}, + + {ResolverStyle.STRICT, 49, 2, 3, 4, null, null}, + {ResolverStyle.SMART, 49, 2, 3, 4, null, null}, + {ResolverStyle.LENIENT, 49, 2, 3, 4, LocalTime.of(1, 2, 3, 4), Period.ofDays(2)}, + + {ResolverStyle.STRICT, -1, 2, 3, 4, null, null}, + {ResolverStyle.SMART, -1, 2, 3, 4, null, null}, + {ResolverStyle.LENIENT, -1, 2, 3, 4, LocalTime.of(23, 2, 3, 4), Period.ofDays(-1)}, + + {ResolverStyle.STRICT, -6, 2, 3, 4, null, null}, + {ResolverStyle.SMART, -6, 2, 3, 4, null, null}, + {ResolverStyle.LENIENT, -6, 2, 3, 4, LocalTime.of(18, 2, 3, 4), Period.ofDays(-1)}, + + {ResolverStyle.STRICT, 25, 61, 61, 1_123456789, null, null}, + {ResolverStyle.SMART, 25, 61, 61, 1_123456789, null, null}, + {ResolverStyle.LENIENT, 25, 61, 61, 1_123456789, LocalTime.of(2, 2, 2, 123456789), Period.ofDays(1)}, + }; + } + + @Test(dataProvider="resolveFourToTime") + public void test_resolveFourToTime(ResolverStyle style, + long hour, long min, long sec, long nano, LocalTime expectedTime, Period excessPeriod) { + DateTimeFormatter f = new DateTimeFormatterBuilder() + .parseDefaulting(HOUR_OF_DAY, hour) + .parseDefaulting(MINUTE_OF_HOUR, min) + .parseDefaulting(SECOND_OF_MINUTE, sec) + .parseDefaulting(NANO_OF_SECOND, nano).toFormatter(); + + ResolverStyle[] styles = (style != null ? new ResolverStyle[] {style} : ResolverStyle.values()); + for (ResolverStyle s : styles) { + if (expectedTime != null) { + TemporalAccessor accessor = f.withResolverStyle(s).parse(""); + assertEquals(accessor.query(TemporalQuery.localDate()), null, "ResolverStyle: " + s); + assertEquals(accessor.query(TemporalQuery.localTime()), expectedTime, "ResolverStyle: " + s); + assertEquals(accessor.query(DateTimeFormatter.parsedExcessDays()), excessPeriod, "ResolverStyle: " + s); + } else { + try { + f.withResolverStyle(style).parse(""); + fail(); + } catch (DateTimeParseException ex) { + // expected + } + } + } + } + + @Test(dataProvider="resolveFourToTime") + public void test_resolveThreeToTime(ResolverStyle style, + long hour, long min, long sec, long nano, LocalTime expectedTime, Period excessPeriod) { + DateTimeFormatter f = new DateTimeFormatterBuilder() + .parseDefaulting(HOUR_OF_DAY, hour) + .parseDefaulting(MINUTE_OF_HOUR, min) + .parseDefaulting(SECOND_OF_MINUTE, sec).toFormatter(); + + ResolverStyle[] styles = (style != null ? new ResolverStyle[] {style} : ResolverStyle.values()); + for (ResolverStyle s : styles) { + if (expectedTime != null) { + TemporalAccessor accessor = f.withResolverStyle(s).parse(""); + assertEquals(accessor.query(TemporalQuery.localDate()), null, "ResolverStyle: " + s); + assertEquals(accessor.query(TemporalQuery.localTime()), expectedTime.minusNanos(nano), "ResolverStyle: " + s); + assertEquals(accessor.query(DateTimeFormatter.parsedExcessDays()), excessPeriod, "ResolverStyle: " + s); + } else { + try { + f.withResolverStyle(style).parse(""); + fail(); + } catch (DateTimeParseException ex) { + // expected + } + } + } + } + + @Test(dataProvider="resolveFourToTime") + public void test_resolveFourToDateTime(ResolverStyle style, + long hour, long min, long sec, long nano, LocalTime expectedTime, Period excessPeriod) { + DateTimeFormatter f = new DateTimeFormatterBuilder() + .parseDefaulting(YEAR, 2012).parseDefaulting(MONTH_OF_YEAR, 6).parseDefaulting(DAY_OF_MONTH, 30) + .parseDefaulting(HOUR_OF_DAY, hour) + .parseDefaulting(MINUTE_OF_HOUR, min) + .parseDefaulting(SECOND_OF_MINUTE, sec) + .parseDefaulting(NANO_OF_SECOND, nano).toFormatter(); + + ResolverStyle[] styles = (style != null ? new ResolverStyle[] {style} : ResolverStyle.values()); + if (expectedTime != null && excessPeriod != null) { + LocalDate expectedDate = LocalDate.of(2012, 6, 30).plus(excessPeriod); + for (ResolverStyle s : styles) { + TemporalAccessor accessor = f.withResolverStyle(s).parse(""); + assertEquals(accessor.query(TemporalQuery.localDate()), expectedDate, "ResolverStyle: " + s); + assertEquals(accessor.query(TemporalQuery.localTime()), expectedTime, "ResolverStyle: " + s); + assertEquals(accessor.query(DateTimeFormatter.parsedExcessDays()), Period.ZERO, "ResolverStyle: " + s); + } + } + } + + //----------------------------------------------------------------------- + @DataProvider(name="resolveSecondOfDay") + Object[][] data_resolveSecondOfDay() { + return new Object[][]{ + {STRICT, 0, 0, 0}, + {STRICT, 1, 1, 0}, + {STRICT, 86399, 86399, 0}, + {STRICT, -1, null, 0}, + {STRICT, 86400, null, 0}, + + {SMART, 0, 0, 0}, + {SMART, 1, 1, 0}, + {SMART, 86399, 86399, 0}, + {SMART, -1, null, 0}, + {SMART, 86400, null, 0}, + + {LENIENT, 0, 0, 0}, + {LENIENT, 1, 1, 0}, + {LENIENT, 86399, 86399, 0}, + {LENIENT, -1, 86399, -1}, + {LENIENT, 86400, 0, 1}, + }; + } + + @Test(dataProvider="resolveSecondOfDay") + public void test_resolveSecondOfDay(ResolverStyle style, long value, Integer expectedSecond, int expectedDays) { + String str = Long.toString(value); + DateTimeFormatter f = new DateTimeFormatterBuilder().appendValue(SECOND_OF_DAY).toFormatter(); + + if (expectedSecond != null) { + TemporalAccessor accessor = f.withResolverStyle(style).parse(str); + assertEquals(accessor.query(TemporalQuery.localDate()), null); + assertEquals(accessor.query(TemporalQuery.localTime()), LocalTime.ofSecondOfDay(expectedSecond)); + assertEquals(accessor.query(DateTimeFormatter.parsedExcessDays()), Period.ofDays(expectedDays)); + } else { + try { + f.withResolverStyle(style).parse(str); + fail(); + } catch (DateTimeParseException ex) { + // expected + } + } + } + + //----------------------------------------------------------------------- + @DataProvider(name="resolveMinuteOfDay") + Object[][] data_resolveMinuteOfDay() { + return new Object[][]{ + {STRICT, 0, 0, 0}, + {STRICT, 1, 1, 0}, + {STRICT, 1439, 1439, 0}, + {STRICT, -1, null, 0}, + {STRICT, 1440, null, 0}, + + {SMART, 0, 0, 0}, + {SMART, 1, 1, 0}, + {SMART, 1439, 1439, 0}, + {SMART, -1, null, 0}, + {SMART, 1440, null, 0}, + + {LENIENT, 0, 0, 0}, + {LENIENT, 1, 1, 0}, + {LENIENT, 1439, 1439, 0}, + {LENIENT, -1, 1439, -1}, + {LENIENT, 1440, 0, 1}, + }; + } + + @Test(dataProvider="resolveMinuteOfDay") + public void test_resolveMinuteOfDay(ResolverStyle style, long value, Integer expectedMinute, int expectedDays) { + String str = Long.toString(value); + DateTimeFormatter f = new DateTimeFormatterBuilder().appendValue(MINUTE_OF_DAY).toFormatter(); + + if (expectedMinute != null) { + TemporalAccessor accessor = f.withResolverStyle(style).parse(str); + assertEquals(accessor.query(TemporalQuery.localDate()), null); + assertEquals(accessor.query(TemporalQuery.localTime()), LocalTime.ofSecondOfDay(expectedMinute * 60)); + assertEquals(accessor.query(DateTimeFormatter.parsedExcessDays()), Period.ofDays(expectedDays)); + } else { + try { + f.withResolverStyle(style).parse(str); + fail(); + } catch (DateTimeParseException ex) { + // expected + } + } + } + + //----------------------------------------------------------------------- + @DataProvider(name="resolveClockHourOfDay") + Object[][] data_resolveClockHourOfDay() { + return new Object[][]{ + {STRICT, 1, 1, 0}, + {STRICT, 24, 0, 0}, + {STRICT, 0, null, 0}, + {STRICT, -1, null, 0}, + {STRICT, 25, null, 0}, + + {SMART, 1, 1, 0}, + {SMART, 24, 0, 0}, + {SMART, 0, 0, 0}, + {SMART, -1, null, 0}, + {SMART, 25, null, 0}, + + {LENIENT, 1, 1, 0}, + {LENIENT, 24, 0, 0}, + {LENIENT, 0, 0, 0}, + {LENIENT, -1, 23, -1}, + {LENIENT, 25, 1, 1}, + }; + } + + @Test(dataProvider="resolveClockHourOfDay") + public void test_resolveClockHourOfDay(ResolverStyle style, long value, Integer expectedHour, int expectedDays) { + String str = Long.toString(value); + DateTimeFormatter f = new DateTimeFormatterBuilder().appendValue(CLOCK_HOUR_OF_DAY).toFormatter(); + + if (expectedHour != null) { + TemporalAccessor accessor = f.withResolverStyle(style).parse(str); + assertEquals(accessor.query(TemporalQuery.localDate()), null); + assertEquals(accessor.query(TemporalQuery.localTime()), LocalTime.of(expectedHour, 0)); + assertEquals(accessor.query(DateTimeFormatter.parsedExcessDays()), Period.ofDays(expectedDays)); + } else { + try { + f.withResolverStyle(style).parse(str); + fail(); + } catch (DateTimeParseException ex) { + // expected + } + } + } + + //----------------------------------------------------------------------- + @DataProvider(name="resolveClockHourOfAmPm") + Object[][] data_resolveClockHourOfAmPm() { + return new Object[][]{ + {STRICT, 1, 1}, + {STRICT, 12, 0}, + {STRICT, 0, null}, + {STRICT, -1, null}, + {STRICT, 13, null}, + + {SMART, 1, 1}, + {SMART, 12, 0}, + {SMART, 0, 0}, + {SMART, -1, null}, + {SMART, 13, null}, + + {LENIENT, 1, 1}, + {LENIENT, 12, 0}, + {LENIENT, 0, 0}, + {LENIENT, -1, -1}, + {LENIENT, 13, 13}, + }; + } + + @Test(dataProvider="resolveClockHourOfAmPm") + public void test_resolveClockHourOfAmPm(ResolverStyle style, long value, Integer expectedValue) { + String str = Long.toString(value); + DateTimeFormatter f = new DateTimeFormatterBuilder().appendValue(CLOCK_HOUR_OF_AMPM).toFormatter(); + + if (expectedValue != null) { + TemporalAccessor accessor = f.withResolverStyle(style).parse(str); + assertEquals(accessor.query(TemporalQuery.localDate()), null); + assertEquals(accessor.query(TemporalQuery.localTime()), null); + assertEquals(accessor.isSupported(CLOCK_HOUR_OF_AMPM), false); + assertEquals(accessor.isSupported(HOUR_OF_AMPM), true); + assertEquals(accessor.getLong(HOUR_OF_AMPM), expectedValue.longValue()); + } else { + try { + f.withResolverStyle(style).parse(str); + fail(); + } catch (DateTimeParseException ex) { + // expected + } + } + } + + //----------------------------------------------------------------------- + @DataProvider(name="resolveAmPm") + Object[][] data_resolveAmPm() { + return new Object[][]{ + {STRICT, 0, 0}, + {STRICT, 1, 1}, + {STRICT, -1, null}, + {STRICT, 2, null}, + + {SMART, 0, 0}, + {SMART, 1, 1}, + {SMART, -1, null}, + {SMART, 2, null}, + + {LENIENT, 0, 0}, + {LENIENT, 1, 1}, + {LENIENT, -1, -1}, + {LENIENT, 2, 2}, + }; + } + + @Test(dataProvider="resolveAmPm") + public void test_resolveAmPm(ResolverStyle style, long value, Integer expectedValue) { + String str = Long.toString(value); + DateTimeFormatter f = new DateTimeFormatterBuilder().appendValue(AMPM_OF_DAY).toFormatter(); + + if (expectedValue != null) { + TemporalAccessor accessor = f.withResolverStyle(style).parse(str); + assertEquals(accessor.query(TemporalQuery.localDate()), null); + assertEquals(accessor.query(TemporalQuery.localTime()), null); + assertEquals(accessor.isSupported(AMPM_OF_DAY), true); + assertEquals(accessor.getLong(AMPM_OF_DAY), expectedValue.longValue()); + } else { + try { + f.withResolverStyle(style).parse(str); + fail(); + } catch (DateTimeParseException ex) { + // expected + } + } + } + } diff --git a/jdk/test/java/time/tck/java/time/format/TCKDateTimeFormatSymbols.java b/jdk/test/java/time/tck/java/time/format/TCKDecimalStyle.java similarity index 77% rename from jdk/test/java/time/tck/java/time/format/TCKDateTimeFormatSymbols.java rename to jdk/test/java/time/tck/java/time/format/TCKDecimalStyle.java index 9deec99814e..f5a4321ad24 100644 --- a/jdk/test/java/time/tck/java/time/format/TCKDateTimeFormatSymbols.java +++ b/jdk/test/java/time/tck/java/time/format/TCKDecimalStyle.java @@ -61,29 +61,30 @@ package tck.java.time.format; import static org.testng.Assert.assertEquals; -import java.time.format.DateTimeFormatSymbols; +import java.time.format.DecimalStyle; import java.util.Arrays; import java.util.Locale; +import java.util.Set; import org.testng.annotations.Test; /** - * Test DateTimeFormatSymbols. + * Test DecimalStyle. */ @Test -public class TCKDateTimeFormatSymbols { +public class TCKDecimalStyle { @Test public void test_getAvailableLocales() { - Locale[] locales = DateTimeFormatSymbols.getAvailableLocales(); - assertEquals(locales.length > 0, true); - assertEquals(Arrays.asList(locales).contains(Locale.US), true); + Set locales = DecimalStyle.getAvailableLocales(); + assertEquals(locales.size() > 0, true, "locales: " + locales); + assertEquals(locales.contains(Locale.US), true, "Locale.US not found in available Locales"); } //----------------------------------------------------------------------- @Test public void test_of_Locale() { - DateTimeFormatSymbols loc1 = DateTimeFormatSymbols.of(Locale.CANADA); + DecimalStyle loc1 = DecimalStyle.of(Locale.CANADA); assertEquals(loc1.getZeroDigit(), '0'); assertEquals(loc1.getPositiveSign(), '+'); assertEquals(loc1.getNegativeSign(), '-'); @@ -93,7 +94,7 @@ public class TCKDateTimeFormatSymbols { //----------------------------------------------------------------------- @Test public void test_STANDARD() { - DateTimeFormatSymbols loc1 = DateTimeFormatSymbols.STANDARD; + DecimalStyle loc1 = DecimalStyle.STANDARD; assertEquals(loc1.getZeroDigit(), '0'); assertEquals(loc1.getPositiveSign(), '+'); assertEquals(loc1.getNegativeSign(), '-'); @@ -103,25 +104,25 @@ public class TCKDateTimeFormatSymbols { //----------------------------------------------------------------------- @Test public void test_zeroDigit() { - DateTimeFormatSymbols base = DateTimeFormatSymbols.STANDARD; + DecimalStyle base = DecimalStyle.STANDARD; assertEquals(base.withZeroDigit('A').getZeroDigit(), 'A'); } @Test public void test_positiveSign() { - DateTimeFormatSymbols base = DateTimeFormatSymbols.STANDARD; + DecimalStyle base = DecimalStyle.STANDARD; assertEquals(base.withPositiveSign('A').getPositiveSign(), 'A'); } @Test public void test_negativeSign() { - DateTimeFormatSymbols base = DateTimeFormatSymbols.STANDARD; + DecimalStyle base = DecimalStyle.STANDARD; assertEquals(base.withNegativeSign('A').getNegativeSign(), 'A'); } @Test public void test_decimalSeparator() { - DateTimeFormatSymbols base = DateTimeFormatSymbols.STANDARD; + DecimalStyle base = DecimalStyle.STANDARD; assertEquals(base.withDecimalSeparator('A').getDecimalSeparator(), 'A'); } @@ -129,7 +130,7 @@ public class TCKDateTimeFormatSymbols { /* TBD: convertToDigit and convertNumberToI18N are package-private methods @Test public void test_convertToDigit_base() { - DateTimeFormatSymbols base = DateTimeFormatSymbols.STANDARD; + DecimalStyle base = DecimalStyle.STANDARD; assertEquals(base.convertToDigit('0'), 0); assertEquals(base.convertToDigit('1'), 1); assertEquals(base.convertToDigit('9'), 9); @@ -139,7 +140,7 @@ public class TCKDateTimeFormatSymbols { @Test public void test_convertToDigit_altered() { - DateTimeFormatSymbols base = DateTimeFormatSymbols.STANDARD.withZeroDigit('A'); + DecimalStyle base = DecimalStyle.STANDARD.withZeroDigit('A'); assertEquals(base.convertToDigit('A'), 0); assertEquals(base.convertToDigit('B'), 1); assertEquals(base.convertToDigit('J'), 9); @@ -150,21 +151,21 @@ public class TCKDateTimeFormatSymbols { //----------------------------------------------------------------------- @Test public void test_convertNumberToI18N_base() { - DateTimeFormatSymbols base = DateTimeFormatSymbols.STANDARD; + DecimalStyle base = DecimalStyle.STANDARD; assertEquals(base.convertNumberToI18N("134"), "134"); } @Test public void test_convertNumberToI18N_altered() { - DateTimeFormatSymbols base = DateTimeFormatSymbols.STANDARD.withZeroDigit('A'); + DecimalStyle base = DecimalStyle.STANDARD.withZeroDigit('A'); assertEquals(base.convertNumberToI18N("134"), "BDE"); } */ //----------------------------------------------------------------------- @Test public void test_equalsHashCode1() { - DateTimeFormatSymbols a = DateTimeFormatSymbols.STANDARD; - DateTimeFormatSymbols b = DateTimeFormatSymbols.STANDARD; + DecimalStyle a = DecimalStyle.STANDARD; + DecimalStyle b = DecimalStyle.STANDARD; assertEquals(a.equals(b), true); assertEquals(b.equals(a), true); assertEquals(a.hashCode(), b.hashCode()); @@ -172,8 +173,8 @@ public class TCKDateTimeFormatSymbols { @Test public void test_equalsHashCode2() { - DateTimeFormatSymbols a = DateTimeFormatSymbols.STANDARD.withZeroDigit('A'); - DateTimeFormatSymbols b = DateTimeFormatSymbols.STANDARD.withZeroDigit('A'); + DecimalStyle a = DecimalStyle.STANDARD.withZeroDigit('A'); + DecimalStyle b = DecimalStyle.STANDARD.withZeroDigit('A'); assertEquals(a.equals(b), true); assertEquals(b.equals(a), true); assertEquals(a.hashCode(), b.hashCode()); @@ -181,15 +182,15 @@ public class TCKDateTimeFormatSymbols { @Test public void test_equalsHashCode3() { - DateTimeFormatSymbols a = DateTimeFormatSymbols.STANDARD.withZeroDigit('A'); - DateTimeFormatSymbols b = DateTimeFormatSymbols.STANDARD.withDecimalSeparator('A'); + DecimalStyle a = DecimalStyle.STANDARD.withZeroDigit('A'); + DecimalStyle b = DecimalStyle.STANDARD.withDecimalSeparator('A'); assertEquals(a.equals(b), false); assertEquals(b.equals(a), false); } @Test public void test_equalsHashCode_bad() { - DateTimeFormatSymbols a = DateTimeFormatSymbols.STANDARD; + DecimalStyle a = DecimalStyle.STANDARD; assertEquals(a.equals(""), false); assertEquals(a.equals(null), false); } @@ -197,14 +198,14 @@ public class TCKDateTimeFormatSymbols { //----------------------------------------------------------------------- @Test public void test_toString_base() { - DateTimeFormatSymbols base = DateTimeFormatSymbols.STANDARD; - assertEquals(base.toString(), "Symbols[0+-.]"); + DecimalStyle base = DecimalStyle.STANDARD; + assertEquals(base.toString(), "DecimalStyle[0+-.]"); } @Test public void test_toString_altered() { - DateTimeFormatSymbols base = DateTimeFormatSymbols.of(Locale.US).withZeroDigit('A').withDecimalSeparator('@'); - assertEquals(base.toString(), "Symbols[A+-@]"); + DecimalStyle base = DecimalStyle.of(Locale.US).withZeroDigit('A').withDecimalSeparator('@'); + assertEquals(base.toString(), "DecimalStyle[A+-@]"); } } diff --git a/jdk/test/java/time/tck/java/time/format/TCKInstantPrinterParser.java b/jdk/test/java/time/tck/java/time/format/TCKInstantPrinterParser.java new file mode 100644 index 00000000000..ebd90505c2a --- /dev/null +++ b/jdk/test/java/time/tck/java/time/format/TCKInstantPrinterParser.java @@ -0,0 +1,286 @@ +/* + * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +/* + * This file is available under and governed by the GNU General Public + * License version 2 only, as published by the Free Software Foundation. + * However, the following notice accompanied the original version of this + * file: + * + * Copyright (c) 2010-2013, Stephen Colebourne & Michael Nascimento Santos + * + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions are met: + * + * * Redistributions of source code must retain the above copyright notice, + * this list of conditions and the following disclaimer. + * + * * Redistributions in binary form must reproduce the above copyright notice, + * this list of conditions and the following disclaimer in the documentation + * and/or other materials provided with the distribution. + * + * * Neither the name of JSR-310 nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR + * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, + * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, + * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR + * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF + * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING + * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS + * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + */ +package tck.java.time.format; + +import static org.testng.Assert.assertEquals; +import static org.testng.Assert.fail; + +import java.time.DateTimeException; +import java.time.Instant; +import java.time.OffsetDateTime; +import java.time.Period; +import java.time.ZoneOffset; +import java.time.format.DateTimeFormatter; +import java.time.format.DateTimeFormatterBuilder; +import java.time.format.ResolverStyle; +import java.time.temporal.TemporalAccessor; + +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; + +/** + * Test DateTimeFormatterBuilder.appendInstant(). + */ +@Test +public class TCKInstantPrinterParser { + + @DataProvider(name="printGrouped") + Object[][] data_printGrouped() { + return new Object[][] { + {0, 0, "1970-01-01T00:00:00Z"}, + + {-1, 0, "1969-12-31T23:59:59Z"}, + {1, 0, "1970-01-01T00:00:01Z"}, + {60, 0, "1970-01-01T00:01:00Z"}, + {3600, 0, "1970-01-01T01:00:00Z"}, + {86400, 0, "1970-01-02T00:00:00Z"}, + + {182, 2, "1970-01-01T00:03:02.000000002Z"}, + {182, 20, "1970-01-01T00:03:02.000000020Z"}, + {182, 200, "1970-01-01T00:03:02.000000200Z"}, + {182, 2000, "1970-01-01T00:03:02.000002Z"}, + {182, 20000, "1970-01-01T00:03:02.000020Z"}, + {182, 200000, "1970-01-01T00:03:02.000200Z"}, + {182, 2000000, "1970-01-01T00:03:02.002Z"}, + {182, 20000000, "1970-01-01T00:03:02.020Z"}, + {182, 200000000, "1970-01-01T00:03:02.200Z"}, + + {Instant.MAX.getEpochSecond(), 999999999, "+1000000000-12-31T23:59:59.999999999Z"}, + {Instant.MIN.getEpochSecond(), 0, "-1000000000-01-01T00:00:00Z"}, + }; + } + + @Test(dataProvider="printGrouped") + public void test_print_grouped(long instantSecs, int nano, String expected) { + Instant instant = Instant.ofEpochSecond(instantSecs, nano); + DateTimeFormatter f = new DateTimeFormatterBuilder().appendInstant().toFormatter(); + assertEquals(f.format(instant), expected); + } + + //----------------------------------------------------------------------- + @DataProvider(name="printDigits") + Object[][] data_printDigits() { + return new Object[][] { + {-1, 0, 0, "1970-01-01T00:00:00Z"}, + {0, 0, 0, "1970-01-01T00:00:00Z"}, + {1, 0, 0, "1970-01-01T00:00:00.0Z"}, + {3, 0, 0, "1970-01-01T00:00:00.000Z"}, + {9, 0, 0, "1970-01-01T00:00:00.000000000Z"}, + + {-1, -1, 0, "1969-12-31T23:59:59Z"}, + {-1, 1, 0, "1970-01-01T00:00:01Z"}, + {-1, 60, 0, "1970-01-01T00:01:00Z"}, + {-1, 3600, 0, "1970-01-01T01:00:00Z"}, + {-1, 86400, 0, "1970-01-02T00:00:00Z"}, + + {-1, 182, 2, "1970-01-01T00:03:02.000000002Z"}, + {-1, 182, 20, "1970-01-01T00:03:02.00000002Z"}, + {-1, 182, 200, "1970-01-01T00:03:02.0000002Z"}, + {-1, 182, 2000, "1970-01-01T00:03:02.000002Z"}, + {-1, 182, 20000, "1970-01-01T00:03:02.00002Z"}, + {-1, 182, 200000, "1970-01-01T00:03:02.0002Z"}, + {-1, 182, 2000000, "1970-01-01T00:03:02.002Z"}, + {-1, 182, 20000000, "1970-01-01T00:03:02.02Z"}, + {-1, 182, 200000000, "1970-01-01T00:03:02.2Z"}, + + {0, 182, 2, "1970-01-01T00:03:02Z"}, + {0, 182, 20, "1970-01-01T00:03:02Z"}, + {0, 182, 200, "1970-01-01T00:03:02Z"}, + {0, 182, 2000, "1970-01-01T00:03:02Z"}, + {0, 182, 20000, "1970-01-01T00:03:02Z"}, + {0, 182, 200000, "1970-01-01T00:03:02Z"}, + {0, 182, 2000000, "1970-01-01T00:03:02Z"}, + {0, 182, 20000000, "1970-01-01T00:03:02Z"}, + {0, 182, 200000000, "1970-01-01T00:03:02Z"}, + + {1, 182, 2, "1970-01-01T00:03:02.0Z"}, + {1, 182, 20, "1970-01-01T00:03:02.0Z"}, + {1, 182, 200, "1970-01-01T00:03:02.0Z"}, + {1, 182, 2000, "1970-01-01T00:03:02.0Z"}, + {1, 182, 20000, "1970-01-01T00:03:02.0Z"}, + {1, 182, 200000, "1970-01-01T00:03:02.0Z"}, + {1, 182, 2000000, "1970-01-01T00:03:02.0Z"}, + {1, 182, 20000000, "1970-01-01T00:03:02.0Z"}, + {1, 182, 200000000, "1970-01-01T00:03:02.2Z"}, + + {3, 182, 2, "1970-01-01T00:03:02.000Z"}, + {3, 182, 20, "1970-01-01T00:03:02.000Z"}, + {3, 182, 200, "1970-01-01T00:03:02.000Z"}, + {3, 182, 2000, "1970-01-01T00:03:02.000Z"}, + {3, 182, 20000, "1970-01-01T00:03:02.000Z"}, + {3, 182, 200000, "1970-01-01T00:03:02.000Z"}, + {3, 182, 2000000, "1970-01-01T00:03:02.002Z"}, + {3, 182, 20000000, "1970-01-01T00:03:02.020Z"}, + {3, 182, 200000000, "1970-01-01T00:03:02.200Z"}, + + {9, 182, 2, "1970-01-01T00:03:02.000000002Z"}, + {9, 182, 20, "1970-01-01T00:03:02.000000020Z"}, + {9, 182, 200, "1970-01-01T00:03:02.000000200Z"}, + {9, 182, 2000, "1970-01-01T00:03:02.000002000Z"}, + {9, 182, 20000, "1970-01-01T00:03:02.000020000Z"}, + {9, 182, 200000, "1970-01-01T00:03:02.000200000Z"}, + {9, 182, 2000000, "1970-01-01T00:03:02.002000000Z"}, + {9, 182, 20000000, "1970-01-01T00:03:02.020000000Z"}, + {9, 182, 200000000, "1970-01-01T00:03:02.200000000Z"}, + + {9, Instant.MAX.getEpochSecond(), 999999999, "+1000000000-12-31T23:59:59.999999999Z"}, + {9, Instant.MIN.getEpochSecond(), 0, "-1000000000-01-01T00:00:00.000000000Z"}, + }; + } + + @Test(dataProvider="printDigits") + public void test_print_digits(int fractionalDigits, long instantSecs, int nano, String expected) { + Instant instant = Instant.ofEpochSecond(instantSecs, nano); + DateTimeFormatter f = new DateTimeFormatterBuilder().appendInstant(fractionalDigits).toFormatter(); + assertEquals(f.format(instant), expected); + } + + //----------------------------------------------------------------------- + @DataProvider(name="parseDigits") + Object[][] data_parse_digits() { + return new Object[][] { + {0, 0, "1970-01-01T00:00:00Z"}, + {0, 0, "1970-01-01T00:00:00Z"}, + {0, 0, "1970-01-01T00:00:00.0Z"}, + {0, 0, "1970-01-01T00:00:00.000Z"}, + {0, 0, "1970-01-01T00:00:00.000000000Z"}, + + {-1, 0, "1969-12-31T23:59:59Z"}, + {1, 0, "1970-01-01T00:00:01Z"}, + {60, 0, "1970-01-01T00:01:00Z"}, + {3600, 0, "1970-01-01T01:00:00Z"}, + {86400, 0, "1970-01-02T00:00:00Z"}, + + {182, 234000000, "1970-01-01T00:03:02.234Z"}, + {182, 234000000, "1970-01-01T00:03:02.2340Z"}, + {182, 234000000, "1970-01-01T00:03:02.23400Z"}, + {182, 234000000, "1970-01-01T00:03:02.234000Z"}, + {182, 234000000, "1970-01-01T00:03:02.234000000Z"}, + + {((23 * 60) + 59) * 60 + 59, 123456789, "1970-01-01T23:59:59.123456789Z"}, + + {Instant.MAX.getEpochSecond(), 999999999, "+1000000000-12-31T23:59:59.999999999Z"}, + {Instant.MIN.getEpochSecond(), 0, "-1000000000-01-01T00:00:00.000000000Z"}, + }; + } + + @Test(dataProvider="parseDigits") + public void test_parse_digitsMinusOne(long instantSecs, int nano, String input) { + Instant expected = Instant.ofEpochSecond(instantSecs, nano); + DateTimeFormatter f = new DateTimeFormatterBuilder().appendInstant(-1).toFormatter(); + assertEquals(f.parse(input, Instant::from), expected); + assertEquals(f.parse(input).query(DateTimeFormatter.parsedExcessDays()), Period.ZERO); + assertEquals(f.parse(input).query(DateTimeFormatter.parsedLeapSecond()), Boolean.FALSE); + } + + @Test(dataProvider="parseDigits") + public void test_parse_digitsNine(long instantSecs, int nano, String input) { + DateTimeFormatter f = new DateTimeFormatterBuilder().appendInstant(9).toFormatter(); + if (input.charAt(input.length() - 11) == '.') { + Instant expected = Instant.ofEpochSecond(instantSecs, nano); + assertEquals(f.parse(input, Instant::from), expected); + assertEquals(f.parse(input).query(DateTimeFormatter.parsedExcessDays()), Period.ZERO); + assertEquals(f.parse(input).query(DateTimeFormatter.parsedLeapSecond()), Boolean.FALSE); + } else { + try { + f.parse(input, Instant::from); + fail(); + } catch (DateTimeException ex) { + // expected + } + } + } + + @Test + public void test_parse_endOfDay() { + Instant expected = OffsetDateTime.of(1970, 2, 4, 0, 0, 0, 0, ZoneOffset.UTC).toInstant(); + DateTimeFormatter f = new DateTimeFormatterBuilder().appendInstant(-1).toFormatter(); + for (ResolverStyle style : ResolverStyle.values()) { + TemporalAccessor parsed = f.withResolverStyle(style).parse("1970-02-03T24:00:00Z"); + assertEquals(parsed.query(Instant::from), expected); + assertEquals(parsed.query(DateTimeFormatter.parsedExcessDays()), Period.ZERO); + assertEquals(parsed.query(DateTimeFormatter.parsedLeapSecond()), Boolean.FALSE); + } + } + + @Test + public void test_parse_leapSecond() { + Instant expected = OffsetDateTime.of(1970, 2, 3, 23, 59, 59, 123456789, ZoneOffset.UTC).toInstant(); + DateTimeFormatter f = new DateTimeFormatterBuilder().appendInstant(-1).toFormatter(); + for (ResolverStyle style : ResolverStyle.values()) { + TemporalAccessor parsed = f.withResolverStyle(style).parse("1970-02-03T23:59:60.123456789Z"); + assertEquals(parsed.query(Instant::from), expected); + assertEquals(parsed.query(DateTimeFormatter.parsedExcessDays()), Period.ZERO); + assertEquals(parsed.query(DateTimeFormatter.parsedLeapSecond()), Boolean.TRUE); + } + } + + //----------------------------------------------------------------------- + @Test(expectedExceptions=IllegalArgumentException.class) + public void test_appendInstant_tooSmall() { + new DateTimeFormatterBuilder().appendInstant(-2); + } + + @Test(expectedExceptions=IllegalArgumentException.class) + public void test_appendInstant_tooBig() { + new DateTimeFormatterBuilder().appendInstant(10); + } + +} diff --git a/jdk/test/java/time/tck/java/time/format/TCKTextStyle.java b/jdk/test/java/time/tck/java/time/format/TCKTextStyle.java index 4e80960fc2f..344a8709ca2 100644 --- a/jdk/test/java/time/tck/java/time/format/TCKTextStyle.java +++ b/jdk/test/java/time/tck/java/time/format/TCKTextStyle.java @@ -67,7 +67,7 @@ import java.time.format.TextStyle; import org.testng.annotations.Test; /** - * Test DateTimeFormatSymbols. + * Test DecimalStyle. */ @Test public class TCKTextStyle { diff --git a/jdk/test/java/time/tck/java/time/temporal/TCKWeekFields.java b/jdk/test/java/time/tck/java/time/temporal/TCKWeekFields.java index 748b5283068..ccb1a4ca7b9 100644 --- a/jdk/test/java/time/tck/java/time/temporal/TCKWeekFields.java +++ b/jdk/test/java/time/tck/java/time/temporal/TCKWeekFields.java @@ -71,7 +71,6 @@ import java.time.LocalDate; import java.time.format.DateTimeFormatter; import java.time.format.DateTimeFormatterBuilder; import java.time.temporal.ChronoUnit; -import java.time.temporal.JulianFields; import java.time.temporal.TemporalField; import java.time.temporal.ValueRange; import java.time.temporal.WeekFields; @@ -161,9 +160,6 @@ public class TCKWeekFields extends AbstractTCKTest { TemporalField dowField = week.dayOfWeek(); TemporalField womField = week.weekOfMonth(); - DayOfWeek isoDOW = day.getDayOfWeek(); - int dow = (7 + isoDOW.getValue() - firstDayOfWeek.getValue()) % 7 + 1; - for (int i = 1; i <= 15; i++) { int actualDOW = day.get(dowField); int actualWOM = day.get(womField); @@ -197,9 +193,6 @@ public class TCKWeekFields extends AbstractTCKTest { TemporalField dowField = week.dayOfWeek(); TemporalField woyField = week.weekOfYear(); - DayOfWeek isoDOW = day.getDayOfWeek(); - int dow = (7 + isoDOW.getValue() - firstDayOfWeek.getValue()) % 7 + 1; - for (int i = 1; i <= 15; i++) { int actualDOW = day.get(dowField); int actualWOY = day.get(woyField); @@ -468,6 +461,28 @@ public class TCKWeekFields extends AbstractTCKTest { @Test(dataProvider="weekFields") public void test_parse_resolve_localizedWoWBY(DayOfWeek firstDayOfWeek, int minDays) { + LocalDate date = LocalDate.of(2012, 12, 31); + WeekFields week = WeekFields.of(firstDayOfWeek, minDays); + TemporalField wowbyField = week.weekOfWeekBasedYear(); + TemporalField yowbyField = week.weekBasedYear(); + + for (int i = 1; i <= 60; i++) { + // Test that with dayOfWeek, week of year and year of week-based-year it computes the date + DateTimeFormatter f = new DateTimeFormatterBuilder() + .appendValue(yowbyField).appendLiteral('-') + .appendValue(wowbyField).appendLiteral('-') + .appendValue(DAY_OF_WEEK).toFormatter(); + String str = date.get(yowbyField) + "-" + date.get(wowbyField) + "-" + + date.get(DAY_OF_WEEK); + LocalDate parsed = LocalDate.parse(str, f); + assertEquals(parsed, date, " :: " + str + " " + i); + + date = date.plusDays(1); + } + } + + @Test(dataProvider="weekFields") + public void test_parse_resolve_localizedWoWBYDow(DayOfWeek firstDayOfWeek, int minDays) { LocalDate date = LocalDate.of(2012, 12, 31); WeekFields week = WeekFields.of(firstDayOfWeek, minDays); TemporalField dowField = week.dayOfWeek(); diff --git a/jdk/test/java/time/test/java/time/chrono/TestChronologyPerf.java b/jdk/test/java/time/test/java/time/chrono/TestChronologyPerf.java index 8d73568b146..e8b3a0083b4 100644 --- a/jdk/test/java/time/test/java/time/chrono/TestChronologyPerf.java +++ b/jdk/test/java/time/test/java/time/chrono/TestChronologyPerf.java @@ -26,6 +26,8 @@ package test.java.time.chrono; import java.time.Duration; import java.time.chrono.Chronology; +import java.time.chrono.HijrahChronology; +import java.time.chrono.HijrahDate; import java.time.temporal.ChronoUnit; import java.util.Set; @@ -42,7 +44,19 @@ public class TestChronologyPerf { Set chronos = Chronology.getAvailableChronologies(); long end = System.nanoTime(); Duration d = Duration.of(end - start, ChronoUnit.NANOS); - System.out.printf(" Duration of Chronology.getAvailableChronologies(): %s%n", d); + System.out.printf(" Cold Duration of Chronology.getAvailableChronologies(): %s%n", d); + + start = System.nanoTime(); + chronos = Chronology.getAvailableChronologies(); + end = System.nanoTime(); + d = Duration.of(end - start, ChronoUnit.NANOS); + System.out.printf(" Warm Duration of Chronology.getAvailableChronologies(): %s%n", d); + + start = System.nanoTime(); + HijrahChronology.INSTANCE.date(1434, 1, 1); + end = System.nanoTime(); + d = Duration.of(end - start, ChronoUnit.NANOS); + System.out.printf(" Warm Duration of HijrahDate.date(1434, 1, 1): %s%n", d); } } diff --git a/jdk/test/java/time/test/java/time/chrono/TestExampleCode.java b/jdk/test/java/time/test/java/time/chrono/TestExampleCode.java index d387c88fd65..cfa478f488d 100644 --- a/jdk/test/java/time/test/java/time/chrono/TestExampleCode.java +++ b/jdk/test/java/time/test/java/time/chrono/TestExampleCode.java @@ -109,21 +109,20 @@ public class TestExampleCode { } //----------------------------------------------------------------------- - // Data provider for Hijrah Variant names + // Data provider for Hijrah Type names //----------------------------------------------------------------------- - @DataProvider(name = "HijrahVariantNames") + @DataProvider(name = "HijrahTypeNames") Object[][] data_of_ummalqura() { return new Object[][]{ - { "Hijrah-umalqura", "islamic", "umalqura"}, + { "Hijrah-umalqura", "islamic-umalqura"}, }; } - @Test(dataProvider= "HijrahVariantNames") - public void test_HijrahVariantViaLocale(String calendarId, String calendarType, String variant) { + @Test(dataProvider= "HijrahTypeNames") + public void test_HijrahTypeViaLocale(String calendarId, String calendarType) { Locale.Builder builder = new Locale.Builder(); builder.setLanguage("en").setRegion("US"); builder.setUnicodeLocaleKeyword("ca", calendarType); - builder.setUnicodeLocaleKeyword("cv", variant); Locale locale = builder.build(); Chronology chrono = Chronology.ofLocale(locale); System.out.printf(" Locale language tag: %s, Chronology ID: %s, type: %s%n", diff --git a/jdk/test/java/time/test/java/time/chrono/TestJapaneseChronology.java b/jdk/test/java/time/test/java/time/chrono/TestJapaneseChronology.java new file mode 100644 index 00000000000..b87555dcb51 --- /dev/null +++ b/jdk/test/java/time/test/java/time/chrono/TestJapaneseChronology.java @@ -0,0 +1,202 @@ +/* + * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved. + * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. + * + * This code is free software; you can redistribute it and/or modify it + * under the terms of the GNU General Public License version 2 only, as + * published by the Free Software Foundation. Oracle designates this + * particular file as subject to the "Classpath" exception as provided + * by Oracle in the LICENSE file that accompanied this code. + * + * This code is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License + * version 2 for more details (a copy is included in the LICENSE file that + * accompanied this code). + * + * You should have received a copy of the GNU General Public License version + * 2 along with this work; if not, write to the Free Software Foundation, + * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA. + * + * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA + * or visit www.oracle.com if you need additional information or have any + * questions. + */ + +package test.java.time.chrono; + +import java.time.*; +import java.time.chrono.*; +import java.time.temporal.*; +import java.util.List; +import java.util.Locale; + +import org.testng.annotations.DataProvider; +import org.testng.annotations.Test; +import static org.testng.Assert.assertEquals; + +/** + * Tests for the Japanese chronology + */ +@Test +public class TestJapaneseChronology { + private static final JapaneseChronology JAPANESE = JapaneseChronology.INSTANCE; + private static final Locale jaJPJP = Locale.forLanguageTag("ja-JP-u-ca-japanese"); + + @DataProvider(name="transitions") + Object[][] transitionData() { + return new Object[][] { + // Japanese era, yearOfEra, month, dayOfMonth, gregorianYear + { JapaneseEra.SEIREKI, Year.MIN_VALUE, 1, 1, Year.MIN_VALUE }, + { JapaneseEra.SEIREKI, 1867, 12, 31, 1867 }, + { JapaneseEra.MEIJI, 1, 1, 25, 1868 }, // Note: the dates of Meiji 1 to 5 are incorrect + { JapaneseEra.MEIJI, 6, 1, 1, 1873 }, + // Meiji-Taisho transition isn't accurate. 1912-07-30 is the last day of Meiji + // and the first day of Taisho. + { JapaneseEra.MEIJI, 45, 7, 29, 1912 }, + { JapaneseEra.TAISHO, 1, 7, 30, 1912 }, + // Same for Taisho-Showa transition. 1926-12-25 is the last day of Taisho + // and the first day of Showa. + { JapaneseEra.TAISHO, 15, 12, 24, 1926 }, + { JapaneseEra.SHOWA, 1, 12, 25, 1926 }, + { JapaneseEra.SHOWA, 64, 1, 7, 1989 }, + { JapaneseEra.HEISEI, 1, 1, 8, 1989 }, + }; + } + + @DataProvider(name="day_year_data") + Object[][] dayYearData() { + return new Object[][] { + // Japanese era, yearOfEra, dayOfYear, month, dayOfMonth + { JapaneseEra.MEIJI, 45, 211, 7, 29 }, + { JapaneseEra.TAISHO, 1, 1, 7, 30 }, + { JapaneseEra.TAISHO, 2, 60, 3, 1 }, + { JapaneseEra.TAISHO, 15, 358, 12, 24 }, + { JapaneseEra.SHOWA, 1, 1, 12, 25 }, + { JapaneseEra.SHOWA, 2, 8, 1, 8 }, + { JapaneseEra.SHOWA, 64, 7, 1, 7 }, + { JapaneseEra.HEISEI, 1, 1, 1, 8 }, + { JapaneseEra.HEISEI, 2, 8, 1, 8 }, + }; + } + + @DataProvider(name="range_data") + Object[][] rangeData() { + return new Object[][] { + // field, minSmallest, minLargest, maxSmallest, maxLargest + { ChronoField.ERA, -999, -999, 2, 2}, + { ChronoField.YEAR_OF_ERA, -999999999, 1, 15, 999999999-1989 }, // depends on the current era + { ChronoField.DAY_OF_YEAR, 1, 1, 7, 366}, + }; + } + + @DataProvider(name="invalid_dates") + Object[][] invalidDatesData() { + return new Object[][] { + // Japanese era, yearOfEra, month, dayOfMonth + { JapaneseEra.SEIREKI, Year.MIN_VALUE - 1, 1, 1 }, + { JapaneseEra.SEIREKI, 1855, 2, 29 }, + { JapaneseEra.SEIREKI, 1868, 1, 25 }, + { JapaneseEra.MEIJI, 6, 2, 29 }, + { JapaneseEra.MEIJI, 45, 7, 30 }, + { JapaneseEra.MEIJI, 46, 1, 1 }, + { JapaneseEra.TAISHO, 1, 7, 29 }, + { JapaneseEra.TAISHO, 2, 2, 29 }, + { JapaneseEra.TAISHO, 15, 12, 25 }, + { JapaneseEra.TAISHO, 16, 1, 1 }, + { JapaneseEra.SHOWA, 1, 12, 24 }, + { JapaneseEra.SHOWA, 2, 2, 29 }, + { JapaneseEra.SHOWA, 64, 1, 8 }, + { JapaneseEra.SHOWA, 65, 1, 1 }, + { JapaneseEra.HEISEI, 1, 1, 7 }, + { JapaneseEra.HEISEI, 1, 2, 29 }, + { JapaneseEra.HEISEI, Year.MAX_VALUE, 12, 31 }, + }; + } + + @DataProvider(name="invalid_eraYear") + Object[][] invalidEraYearData() { + return new Object[][] { + // Japanese era, yearOfEra + { JapaneseEra.SEIREKI, Year.MIN_VALUE - 1 }, + { JapaneseEra.SEIREKI, 2012 }, + { JapaneseEra.MEIJI, -1 }, + { JapaneseEra.MEIJI, 0 }, + { JapaneseEra.MEIJI, 46 }, + { JapaneseEra.TAISHO, -1 }, + { JapaneseEra.TAISHO, 0 }, + { JapaneseEra.TAISHO, 16 }, + { JapaneseEra.SHOWA, -1 }, + { JapaneseEra.SHOWA, 0 }, + { JapaneseEra.SHOWA, 65 }, + { JapaneseEra.HEISEI, -1 }, + { JapaneseEra.HEISEI, 0 }, + { JapaneseEra.HEISEI, Year.MAX_VALUE }, + }; + } + + @DataProvider(name="invalid_day_year_data") + Object[][] invalidDayYearData() { + return new Object[][] { + // Japanese era, yearOfEra, dayOfYear + { JapaneseEra.MEIJI, 45, 240 }, + { JapaneseEra.TAISHO, 1, 365 }, + { JapaneseEra.TAISHO, 2, 366 }, + { JapaneseEra.TAISHO, 15, 359 }, + { JapaneseEra.SHOWA, 1, 8 }, + { JapaneseEra.SHOWA, 2, 366 }, + { JapaneseEra.SHOWA, 64, 8 }, + { JapaneseEra.HEISEI, 1, 360 }, + { JapaneseEra.HEISEI, 2, 366 }, + }; + } + + @Test + public void test_ofLocale() { + // must be a singleton + assertEquals(Chronology.ofLocale(jaJPJP) == JAPANESE, true); + } + + @Test(dataProvider="transitions") + public void test_transitions(JapaneseEra era, int yearOfEra, int month, int dayOfMonth, int gregorianYear) { + assertEquals(JAPANESE.prolepticYear(era, yearOfEra), gregorianYear); + + JapaneseDate jdate1 = JapaneseDate.of(era, yearOfEra, month, dayOfMonth); + JapaneseDate jdate2 = JapaneseDate.of(gregorianYear, month, dayOfMonth); + assertEquals(jdate1, jdate2); + } + + @Test(dataProvider="range_data") + public void test_range(ChronoField field, int minSmallest, int minLargest, int maxSmallest, int maxLargest) { + ValueRange range = JAPANESE.range(field); + assertEquals(range.getMinimum(), minSmallest); + assertEquals(range.getLargestMinimum(), minLargest); + assertEquals(range.getSmallestMaximum(), maxSmallest); + assertEquals(range.getMaximum(), maxLargest); + } + + @Test(dataProvider="day_year_data") + public void test_firstDayOfEra(JapaneseEra era, int yearOfEra, int dayOfYear, int month, int dayOfMonth) { + JapaneseDate date1 = JAPANESE.dateYearDay(era, yearOfEra, dayOfYear); + JapaneseDate date2 = JAPANESE.date(era, yearOfEra, month, dayOfMonth); + assertEquals(date1, date2); + } + + @Test(dataProvider="invalid_dates", expectedExceptions=DateTimeException.class) + public void test_invalidDate(JapaneseEra era, int yearOfEra, int month, int dayOfMonth) { + JapaneseDate jdate = JapaneseDate.of(era, yearOfEra, month, dayOfMonth); + System.out.printf("No DateTimeException with %s %d.%02d.%02d%n", era, yearOfEra, month, dayOfMonth); + } + + @Test(dataProvider="invalid_eraYear", expectedExceptions=DateTimeException.class) + public void test_invalidEraYear(JapaneseEra era, int yearOfEra) { + int year = JAPANESE.prolepticYear(era, yearOfEra); + System.out.printf("No DateTimeException with era=%s, year=%d%n", era, yearOfEra); + } + + @Test(dataProvider="invalid_day_year_data", expectedExceptions=DateTimeException.class) + public void test_invalidDayYear(JapaneseEra era, int yearOfEra, int dayOfYear) { + JapaneseDate date = JAPANESE.dateYearDay(era, yearOfEra, dayOfYear); + System.out.printf("No DateTimeException with era=%s, year=%d, dayOfYear=%d%n", era, yearOfEra, dayOfYear); + } +} diff --git a/jdk/test/java/time/test/java/time/format/AbstractTestPrinterParser.java b/jdk/test/java/time/test/java/time/format/AbstractTestPrinterParser.java index e11a24dd0c7..023f5c190ad 100644 --- a/jdk/test/java/time/test/java/time/format/AbstractTestPrinterParser.java +++ b/jdk/test/java/time/test/java/time/format/AbstractTestPrinterParser.java @@ -63,7 +63,7 @@ import java.time.DateTimeException; import java.time.LocalDateTime; import java.time.ZoneId; import java.time.ZonedDateTime; -import java.time.format.DateTimeFormatSymbols; +import java.time.format.DecimalStyle; import java.time.format.DateTimeFormatter; import java.time.format.DateTimeFormatterBuilder; import java.time.format.SignStyle; @@ -85,7 +85,7 @@ public class AbstractTestPrinterParser { protected DateTimeFormatterBuilder builder; protected TemporalAccessor dta; protected Locale locale; - protected DateTimeFormatSymbols symbols; + protected DecimalStyle decimalStyle; @BeforeMethod @@ -94,7 +94,7 @@ public class AbstractTestPrinterParser { builder = new DateTimeFormatterBuilder(); dta = ZonedDateTime.of(LocalDateTime.of(2011, 6, 30, 12, 30, 40, 0), ZoneId.of("Europe/Paris")); locale = Locale.ENGLISH; - symbols = DateTimeFormatSymbols.STANDARD; + decimalStyle = DecimalStyle.STANDARD; } protected void setCaseSensitive(boolean caseSensitive) { @@ -114,35 +114,35 @@ public class AbstractTestPrinterParser { } protected DateTimeFormatter getFormatter() { - return builder.toFormatter(locale).withSymbols(symbols); + return builder.toFormatter(locale).withDecimalStyle(decimalStyle); } protected DateTimeFormatter getFormatter(char c) { - return builder.appendLiteral(c).toFormatter(locale).withSymbols(symbols); + return builder.appendLiteral(c).toFormatter(locale).withDecimalStyle(decimalStyle); } protected DateTimeFormatter getFormatter(String s) { - return builder.appendLiteral(s).toFormatter(locale).withSymbols(symbols); + return builder.appendLiteral(s).toFormatter(locale).withDecimalStyle(decimalStyle); } protected DateTimeFormatter getFormatter(TemporalField field) { - return builder.appendText(field).toFormatter(locale).withSymbols(symbols); + return builder.appendText(field).toFormatter(locale).withDecimalStyle(decimalStyle); } protected DateTimeFormatter getFormatter(TemporalField field, TextStyle style) { - return builder.appendText(field, style).toFormatter(locale).withSymbols(symbols); + return builder.appendText(field, style).toFormatter(locale).withDecimalStyle(decimalStyle); } protected DateTimeFormatter getFormatter(TemporalField field, int minWidth, int maxWidth, SignStyle signStyle) { - return builder.appendValue(field, minWidth, maxWidth, signStyle).toFormatter(locale).withSymbols(symbols); + return builder.appendValue(field, minWidth, maxWidth, signStyle).toFormatter(locale).withDecimalStyle(decimalStyle); } protected DateTimeFormatter getFormatter(String pattern, String noOffsetText) { - return builder.appendOffset(pattern, noOffsetText).toFormatter(locale).withSymbols(symbols); + return builder.appendOffset(pattern, noOffsetText).toFormatter(locale).withDecimalStyle(decimalStyle); } protected DateTimeFormatter getPatternFormatter(String pattern) { - return builder.appendPattern(pattern).toFormatter(locale).withSymbols(symbols); + return builder.appendPattern(pattern).toFormatter(locale).withDecimalStyle(decimalStyle); } protected static final TemporalAccessor EMPTY_DTA = new TemporalAccessor() { diff --git a/jdk/test/java/time/test/java/time/format/TestDateTimeFormatter.java b/jdk/test/java/time/test/java/time/format/TestDateTimeFormatter.java index a96dfb6b455..540a0e89a16 100644 --- a/jdk/test/java/time/test/java/time/format/TestDateTimeFormatter.java +++ b/jdk/test/java/time/test/java/time/format/TestDateTimeFormatter.java @@ -62,7 +62,7 @@ package test.java.time.format; import static java.time.temporal.ChronoField.DAY_OF_MONTH; import static org.testng.Assert.assertSame; -import java.time.format.DateTimeFormatSymbols; +import java.time.format.DecimalStyle; import java.time.format.DateTimeFormatter; import java.time.format.DateTimeFormatterBuilder; import java.time.format.SignStyle; @@ -82,7 +82,7 @@ public class TestDateTimeFormatter { new DateTimeFormatterBuilder().appendLiteral("ONE") .appendValue(DAY_OF_MONTH, 1, 2, SignStyle.NOT_NEGATIVE) .toFormatter(Locale.ENGLISH) - .withSymbols(DateTimeFormatSymbols.STANDARD); + .withDecimalStyle(DecimalStyle.STANDARD); DateTimeFormatter test = base.withLocale(Locale.ENGLISH); assertSame(test, base); } diff --git a/jdk/test/java/time/test/java/time/format/TestDateTimeFormatterBuilder.java b/jdk/test/java/time/test/java/time/format/TestDateTimeFormatterBuilder.java index 432c0381f06..bdaf22be8f7 100644 --- a/jdk/test/java/time/test/java/time/format/TestDateTimeFormatterBuilder.java +++ b/jdk/test/java/time/test/java/time/format/TestDateTimeFormatterBuilder.java @@ -65,13 +65,19 @@ import static java.time.temporal.ChronoField.MINUTE_OF_HOUR; import static java.time.temporal.ChronoField.MONTH_OF_YEAR; import static java.time.temporal.ChronoField.YEAR; import static org.testng.Assert.assertEquals; +import static org.testng.Assert.assertNotNull; import java.text.ParsePosition; import java.time.LocalDate; import java.time.YearMonth; import java.time.ZoneOffset; +import java.time.chrono.Chronology; +import java.time.chrono.IsoChronology; +import java.time.chrono.JapaneseChronology; +import java.time.chrono.MinguoChronology; import java.time.format.DateTimeFormatter; import java.time.format.DateTimeFormatterBuilder; +import java.time.format.FormatStyle; import java.time.format.SignStyle; import java.time.format.TextStyle; import java.time.temporal.Temporal; @@ -268,7 +274,7 @@ public class TestDateTimeFormatterBuilder { public void test_appendValueReduced() throws Exception { builder.appendValueReduced(YEAR, 2, 2000); DateTimeFormatter f = builder.toFormatter(); - assertEquals(f.toString(), "ReducedValue(Year,2,2000)"); + assertEquals(f.toString(), "ReducedValue(Year,2,2,2000)"); TemporalAccessor parsed = f.parseUnresolved("12", new ParsePosition(0)); assertEquals(parsed.getLong(YEAR), 2012L); } @@ -277,8 +283,10 @@ public class TestDateTimeFormatterBuilder { public void test_appendValueReduced_subsequent_parse() throws Exception { builder.appendValue(MONTH_OF_YEAR, 1, 2, SignStyle.NORMAL).appendValueReduced(YEAR, 2, 2000); DateTimeFormatter f = builder.toFormatter(); - assertEquals(f.toString(), "Value(MonthOfYear,1,2,NORMAL)ReducedValue(Year,2,2000)"); - TemporalAccessor parsed = f.parseUnresolved("123", new ParsePosition(0)); + assertEquals(f.toString(), "Value(MonthOfYear,1,2,NORMAL)ReducedValue(Year,2,2,2000)"); + ParsePosition ppos = new ParsePosition(0); + TemporalAccessor parsed = f.parseUnresolved("123", ppos); + assertNotNull(parsed, "Parse failed: " + ppos.toString()); assertEquals(parsed.getLong(MONTH_OF_YEAR), 1L); assertEquals(parsed.getLong(YEAR), 2023L); } @@ -646,13 +654,13 @@ public class TestDateTimeFormatterBuilder { {"GGGGG", "Text(Era,NARROW)"}, {"u", "Value(Year)"}, - {"uu", "ReducedValue(Year,2,2000)"}, + {"uu", "ReducedValue(Year,2,2,2000)"}, {"uuu", "Value(Year,3,19,NORMAL)"}, {"uuuu", "Value(Year,4,19,EXCEEDS_PAD)"}, {"uuuuu", "Value(Year,5,19,EXCEEDS_PAD)"}, {"y", "Value(YearOfEra)"}, - {"yy", "ReducedValue(YearOfEra,2,2000)"}, + {"yy", "ReducedValue(YearOfEra,2,2,2000)"}, {"yyy", "Value(YearOfEra,3,19,NORMAL)"}, {"yyyy", "Value(YearOfEra,4,19,EXCEEDS_PAD)"}, {"yyyyy", "Value(YearOfEra,5,19,EXCEEDS_PAD)"}, @@ -892,6 +900,109 @@ public class TestDateTimeFormatterBuilder { assertEquals(test, expected); } + //----------------------------------------------------------------------- + @DataProvider(name="localePatterns") + Object[][] localizedDateTimePatterns() { + return new Object[][] { + {FormatStyle.FULL, FormatStyle.FULL, IsoChronology.INSTANCE, Locale.US, "EEEE, MMMM d, yyyy h:mm:ss a z"}, + {FormatStyle.LONG, FormatStyle.LONG, IsoChronology.INSTANCE, Locale.US, "MMMM d, yyyy h:mm:ss a z"}, + {FormatStyle.MEDIUM, FormatStyle.MEDIUM, IsoChronology.INSTANCE, Locale.US, "MMM d, yyyy h:mm:ss a"}, + {FormatStyle.SHORT, FormatStyle.SHORT, IsoChronology.INSTANCE, Locale.US, "M/d/yy h:mm a"}, + {FormatStyle.FULL, null, IsoChronology.INSTANCE, Locale.US, "EEEE, MMMM d, yyyy"}, + {FormatStyle.LONG, null, IsoChronology.INSTANCE, Locale.US, "MMMM d, yyyy"}, + {FormatStyle.MEDIUM, null, IsoChronology.INSTANCE, Locale.US, "MMM d, yyyy"}, + {FormatStyle.SHORT, null, IsoChronology.INSTANCE, Locale.US, "M/d/yy"}, + {null, FormatStyle.FULL, IsoChronology.INSTANCE, Locale.US, "h:mm:ss a z"}, + {null, FormatStyle.LONG, IsoChronology.INSTANCE, Locale.US, "h:mm:ss a z"}, + {null, FormatStyle.MEDIUM, IsoChronology.INSTANCE, Locale.US, "h:mm:ss a"}, + {null, FormatStyle.SHORT, IsoChronology.INSTANCE, Locale.US, "h:mm a"}, + + // French Locale and ISO Chronology + {FormatStyle.FULL, FormatStyle.FULL, IsoChronology.INSTANCE, Locale.FRENCH, "EEEE d MMMM yyyy HH' h 'mm z"}, + {FormatStyle.LONG, FormatStyle.LONG, IsoChronology.INSTANCE, Locale.FRENCH, "d MMMM yyyy HH:mm:ss z"}, + {FormatStyle.MEDIUM, FormatStyle.MEDIUM, IsoChronology.INSTANCE, Locale.FRENCH, "d MMM yyyy HH:mm:ss"}, + {FormatStyle.SHORT, FormatStyle.SHORT, IsoChronology.INSTANCE, Locale.FRENCH, "dd/MM/yy HH:mm"}, + {FormatStyle.FULL, null, IsoChronology.INSTANCE, Locale.FRENCH, "EEEE d MMMM yyyy"}, + {FormatStyle.LONG, null, IsoChronology.INSTANCE, Locale.FRENCH, "d MMMM yyyy"}, + {FormatStyle.MEDIUM, null, IsoChronology.INSTANCE, Locale.FRENCH, "d MMM yyyy"}, + {FormatStyle.SHORT, null, IsoChronology.INSTANCE, Locale.FRENCH, "dd/MM/yy"}, + {null, FormatStyle.FULL, IsoChronology.INSTANCE, Locale.FRENCH, "HH' h 'mm z"}, + {null, FormatStyle.LONG, IsoChronology.INSTANCE, Locale.FRENCH, "HH:mm:ss z"}, + {null, FormatStyle.MEDIUM, IsoChronology.INSTANCE, Locale.FRENCH, "HH:mm:ss"}, + {null, FormatStyle.SHORT, IsoChronology.INSTANCE, Locale.FRENCH, "HH:mm"}, + + // Japanese Locale and JapaneseChronology + {FormatStyle.FULL, FormatStyle.FULL, JapaneseChronology.INSTANCE, Locale.JAPANESE, "Gy'\u5e74'M'\u6708'd'\u65e5' H'\u6642'mm'\u5206'ss'\u79d2' z"}, + {FormatStyle.LONG, FormatStyle.LONG, JapaneseChronology.INSTANCE, Locale.JAPANESE, "GGGGGy.MM.dd H:mm:ss z"}, + {FormatStyle.MEDIUM, FormatStyle.MEDIUM, JapaneseChronology.INSTANCE, Locale.JAPANESE, "GGGGGy.MM.dd H:mm:ss"}, + {FormatStyle.SHORT, FormatStyle.SHORT, JapaneseChronology.INSTANCE, Locale.JAPANESE, "GGGGGy.MM.dd H:mm"}, + {FormatStyle.FULL, null, JapaneseChronology.INSTANCE, Locale.JAPANESE, "Gy'\u5e74'M'\u6708'd'\u65e5'"}, + {FormatStyle.LONG, null, JapaneseChronology.INSTANCE, Locale.JAPANESE, "GGGGGy.MM.dd"}, + {FormatStyle.MEDIUM, null, JapaneseChronology.INSTANCE, Locale.JAPANESE, "GGGGGy.MM.dd"}, + {FormatStyle.SHORT, null, JapaneseChronology.INSTANCE, Locale.JAPANESE, "GGGGGy.MM.dd"}, + {null, FormatStyle.FULL, JapaneseChronology.INSTANCE, Locale.JAPANESE, "H'\u6642'mm'\u5206'ss'\u79d2' z"}, + {null, FormatStyle.LONG, JapaneseChronology.INSTANCE, Locale.JAPANESE, "H:mm:ss z"}, + {null, FormatStyle.MEDIUM, JapaneseChronology.INSTANCE, Locale.JAPANESE, "H:mm:ss"}, + {null, FormatStyle.SHORT, JapaneseChronology.INSTANCE, Locale.JAPANESE, "H:mm"}, + + // Chinese Local and Chronology + {FormatStyle.FULL, FormatStyle.FULL, MinguoChronology.INSTANCE, Locale.CHINESE, "Gy\u5e74M\u6708d\u65e5EEEE ahh'\u65f6'mm'\u5206'ss'\u79d2' z"}, + {FormatStyle.LONG, FormatStyle.LONG, MinguoChronology.INSTANCE, Locale.CHINESE, "Gy\u5e74M\u6708d\u65e5 ahh'\u65f6'mm'\u5206'ss'\u79d2'"}, + {FormatStyle.MEDIUM, FormatStyle.MEDIUM, MinguoChronology.INSTANCE, Locale.CHINESE, "Gy-M-d H:mm:ss"}, + {FormatStyle.SHORT, FormatStyle.SHORT, MinguoChronology.INSTANCE, Locale.CHINESE, "Gy-M-d ah:mm"}, + {FormatStyle.FULL, null, MinguoChronology.INSTANCE, Locale.CHINESE, "Gy\u5e74M\u6708d\u65e5EEEE"}, + {FormatStyle.LONG, null, MinguoChronology.INSTANCE, Locale.CHINESE, "Gy\u5e74M\u6708d\u65e5"}, + {FormatStyle.MEDIUM, null, MinguoChronology.INSTANCE, Locale.CHINESE, "Gy-M-d"}, + {FormatStyle.SHORT, null, MinguoChronology.INSTANCE, Locale.CHINESE, "Gy-M-d"}, + {null, FormatStyle.FULL, MinguoChronology.INSTANCE, Locale.CHINESE, "ahh'\u65f6'mm'\u5206'ss'\u79d2' z"}, + {null, FormatStyle.LONG, MinguoChronology.INSTANCE, Locale.CHINESE, "ahh'\u65f6'mm'\u5206'ss'\u79d2'"}, + {null, FormatStyle.MEDIUM, MinguoChronology.INSTANCE, Locale.CHINESE, "H:mm:ss"}, + {null, FormatStyle.SHORT, MinguoChronology.INSTANCE, Locale.CHINESE, "ah:mm"}, + }; + } + + @Test(dataProvider="localePatterns") + public void test_getLocalizedDateTimePattern(FormatStyle dateStyle, FormatStyle timeStyle, + Chronology chrono, Locale locale, String expected) { + String actual = DateTimeFormatterBuilder.getLocalizedDateTimePattern(dateStyle, timeStyle, chrono, locale); + assertEquals(actual, expected, "Pattern " + convertNonAscii(actual)); + } + + @Test(expectedExceptions=java.lang.IllegalArgumentException.class) + public void test_getLocalizedDateTimePatternIAE() { + DateTimeFormatterBuilder.getLocalizedDateTimePattern(null, null, IsoChronology.INSTANCE, Locale.US); + } + + @Test(expectedExceptions=java.lang.NullPointerException.class) + public void test_getLocalizedChronoNPE() { + DateTimeFormatterBuilder.getLocalizedDateTimePattern(FormatStyle.SHORT, FormatStyle.SHORT, null, Locale.US); + } + + @Test(expectedExceptions=java.lang.NullPointerException.class) + public void test_getLocalizedLocaleNPE() { + DateTimeFormatterBuilder.getLocalizedDateTimePattern(FormatStyle.SHORT, FormatStyle.SHORT, IsoChronology.INSTANCE, null); + } + + /** + * Returns a string that includes non-ascii characters after expanding + * the non-ascii characters to their Java language \\uxxxx form. + * @param input an input string + * @return the encoded string. + */ + private String convertNonAscii(String input) { + StringBuilder sb = new StringBuilder(input.length() * 6); + for (int i = 0; i < input.length(); i++) { + char ch = input.charAt(i); + if (ch < 255) { + sb.append(ch); + } else { + sb.append("\\u"); + sb.append(Integer.toHexString(ch)); + } + } + return sb.toString(); + } + private static Temporal date(int y, int m, int d) { return LocalDate.of(y, m, d); } diff --git a/jdk/test/java/time/test/java/time/format/TestDateTimeFormatSymbols.java b/jdk/test/java/time/test/java/time/format/TestDecimalStyle.java similarity index 88% rename from jdk/test/java/time/test/java/time/format/TestDateTimeFormatSymbols.java rename to jdk/test/java/time/test/java/time/format/TestDecimalStyle.java index cb3932a3932..b506d547ada 100644 --- a/jdk/test/java/time/test/java/time/format/TestDateTimeFormatSymbols.java +++ b/jdk/test/java/time/test/java/time/format/TestDecimalStyle.java @@ -61,29 +61,29 @@ package test.java.time.format; import static org.testng.Assert.assertSame; -import java.time.format.DateTimeFormatSymbols; +import java.time.format.DecimalStyle; import java.util.Locale; import org.testng.annotations.Test; /** - * Test DateTimeFormatSymbols. + * Test DecimalStyle. */ @Test -public class TestDateTimeFormatSymbols { +public class TestDecimalStyle { @Test public void test_of_Locale_cached() { - DateTimeFormatSymbols loc1 = DateTimeFormatSymbols.of(Locale.CANADA); - DateTimeFormatSymbols loc2 = DateTimeFormatSymbols.of(Locale.CANADA); + DecimalStyle loc1 = DecimalStyle.of(Locale.CANADA); + DecimalStyle loc2 = DecimalStyle.of(Locale.CANADA); assertSame(loc1, loc2); } //----------------------------------------------------------------------- @Test public void test_ofDefaultLocale_cached() { - DateTimeFormatSymbols loc1 = DateTimeFormatSymbols.ofDefaultLocale(); - DateTimeFormatSymbols loc2 = DateTimeFormatSymbols.ofDefaultLocale(); + DecimalStyle loc1 = DecimalStyle.ofDefaultLocale(); + DecimalStyle loc2 = DecimalStyle.ofDefaultLocale(); assertSame(loc1, loc2); } diff --git a/jdk/test/java/time/test/java/time/format/TestFractionPrinterParser.java b/jdk/test/java/time/test/java/time/format/TestFractionPrinterParser.java index ae940540f6c..c476772ba9b 100644 --- a/jdk/test/java/time/test/java/time/format/TestFractionPrinterParser.java +++ b/jdk/test/java/time/test/java/time/format/TestFractionPrinterParser.java @@ -82,7 +82,7 @@ import test.java.time.temporal.MockFieldValue; public class TestFractionPrinterParser extends AbstractTestPrinterParser { private DateTimeFormatter getFormatter(TemporalField field, int minWidth, int maxWidth, boolean decimalPoint) { - return builder.appendFraction(field, minWidth, maxWidth, decimalPoint).toFormatter(locale).withSymbols(symbols); + return builder.appendFraction(field, minWidth, maxWidth, decimalPoint).toFormatter(locale).withDecimalStyle(decimalStyle); } //----------------------------------------------------------------------- diff --git a/jdk/test/java/time/test/java/time/format/TestNonIsoFormatter.java b/jdk/test/java/time/test/java/time/format/TestNonIsoFormatter.java index 24852eff096..49ec4d6554f 100644 --- a/jdk/test/java/time/test/java/time/format/TestNonIsoFormatter.java +++ b/jdk/test/java/time/test/java/time/format/TestNonIsoFormatter.java @@ -32,7 +32,7 @@ import java.time.chrono.IsoChronology; import java.time.chrono.JapaneseChronology; import java.time.chrono.MinguoChronology; import java.time.chrono.ThaiBuddhistChronology; -import java.time.format.DateTimeFormatSymbols; +import java.time.format.DecimalStyle; import java.time.format.DateTimeFormatter; import java.time.format.DateTimeFormatterBuilder; import java.time.format.DateTimeParseException; @@ -129,7 +129,7 @@ public class TestNonIsoFormatter { ChronoLocalDate date, String expected) { DateTimeFormatter dtf = DateTimeFormatter.ofLocalizedDate(FormatStyle.FULL) .withChronology(chrono).withLocale(formatLocale) - .withSymbols(DateTimeFormatSymbols.of(numberingLocale)); + .withDecimalStyle(DecimalStyle.of(numberingLocale)); String text = dtf.format(date); assertEquals(text, expected); } @@ -139,7 +139,7 @@ public class TestNonIsoFormatter { ChronoLocalDate expected, String text) { DateTimeFormatter dtf = DateTimeFormatter.ofLocalizedDate(FormatStyle.FULL) .withChronology(chrono).withLocale(formatLocale) - .withSymbols(DateTimeFormatSymbols.of(numberingLocale)); + .withDecimalStyle(DecimalStyle.of(numberingLocale)); TemporalAccessor temporal = dtf.parse(text); ChronoLocalDate date = chrono.date(temporal); assertEquals(date, expected); diff --git a/jdk/test/java/time/test/java/time/format/TestNumberParser.java b/jdk/test/java/time/test/java/time/format/TestNumberParser.java index a6fc4a2ee47..3e657582185 100644 --- a/jdk/test/java/time/test/java/time/format/TestNumberParser.java +++ b/jdk/test/java/time/test/java/time/format/TestNumberParser.java @@ -169,7 +169,7 @@ public class TestNumberParser extends AbstractTestPrinterParser { DateTimeFormatter dtf = getFormatter(DAY_OF_MONTH, minWidth, maxWidth, signStyle); if (subsequentWidth > 0) { // hacky, to reserve space - dtf = builder.appendValue(DAY_OF_YEAR, subsequentWidth).toFormatter(locale).withSymbols(symbols); + dtf = builder.appendValue(DAY_OF_YEAR, subsequentWidth).toFormatter(locale).withDecimalStyle(decimalStyle); } TemporalAccessor parsed = dtf.parseUnresolved(text, ppos); if (ppos.getErrorIndex() != -1) { @@ -189,7 +189,7 @@ public class TestNumberParser extends AbstractTestPrinterParser { DateTimeFormatter dtf = getFormatter(DAY_OF_WEEK, minWidth, maxWidth, signStyle); if (subsequentWidth > 0) { // hacky, to reserve space - dtf = builder.appendValue(DAY_OF_YEAR, subsequentWidth).toFormatter(locale).withSymbols(symbols); + dtf = builder.appendValue(DAY_OF_YEAR, subsequentWidth).toFormatter(locale).withDecimalStyle(decimalStyle); } TemporalAccessor parsed = dtf.parseUnresolved(text, ppos); if (ppos.getErrorIndex() != -1) { @@ -326,16 +326,16 @@ public class TestNumberParser extends AbstractTestPrinterParser { {"0", 1, 2, SignStyle.NEVER, 1, 0}, {"5", 1, 2, SignStyle.NEVER, 1, 5}, {"50", 1, 2, SignStyle.NEVER, 2, 50}, - {"500", 1, 2, SignStyle.NEVER, 2, 50}, + {"500", 1, 2, SignStyle.NEVER, 3, 500}, {"-0", 1, 2, SignStyle.NEVER, 2, 0}, {"-5", 1, 2, SignStyle.NEVER, 2, -5}, {"-50", 1, 2, SignStyle.NEVER, 3, -50}, - {"-500", 1, 2, SignStyle.NEVER, 3, -50}, + {"-500", 1, 2, SignStyle.NEVER, 4, -500}, {"-AAA", 1, 2, SignStyle.NEVER, 1, null}, {"+0", 1, 2, SignStyle.NEVER, 2, 0}, {"+5", 1, 2, SignStyle.NEVER, 2, 5}, {"+50", 1, 2, SignStyle.NEVER, 3, 50}, - {"+500", 1, 2, SignStyle.NEVER, 3, 50}, + {"+500", 1, 2, SignStyle.NEVER, 4, 500}, {"+AAA", 1, 2, SignStyle.NEVER, 1, null}, {"50", 2, 2, SignStyle.NEVER, 2, 50}, {"-50", 2, 2, SignStyle.NEVER, 0, null}, @@ -345,16 +345,16 @@ public class TestNumberParser extends AbstractTestPrinterParser { {"0", 1, 2, SignStyle.NOT_NEGATIVE, 1, 0}, {"5", 1, 2, SignStyle.NOT_NEGATIVE, 1, 5}, {"50", 1, 2, SignStyle.NOT_NEGATIVE, 2, 50}, - {"500", 1, 2, SignStyle.NOT_NEGATIVE, 2, 50}, + {"500", 1, 2, SignStyle.NOT_NEGATIVE, 3, 500}, {"-0", 1, 2, SignStyle.NOT_NEGATIVE, 2, 0}, {"-5", 1, 2, SignStyle.NOT_NEGATIVE, 2, -5}, {"-50", 1, 2, SignStyle.NOT_NEGATIVE, 3, -50}, - {"-500", 1, 2, SignStyle.NOT_NEGATIVE, 3, -50}, + {"-500", 1, 2, SignStyle.NOT_NEGATIVE, 4, -500}, {"-AAA", 1, 2, SignStyle.NOT_NEGATIVE, 1, null}, {"+0", 1, 2, SignStyle.NOT_NEGATIVE, 2, 0}, {"+5", 1, 2, SignStyle.NOT_NEGATIVE, 2, 5}, {"+50", 1, 2, SignStyle.NOT_NEGATIVE, 3, 50}, - {"+500", 1, 2, SignStyle.NOT_NEGATIVE, 3, 50}, + {"+500", 1, 2, SignStyle.NOT_NEGATIVE, 4, 500}, {"+AAA", 1, 2, SignStyle.NOT_NEGATIVE, 1, null}, {"50", 2, 2, SignStyle.NOT_NEGATIVE, 2, 50}, {"-50", 2, 2, SignStyle.NOT_NEGATIVE, 0, null}, @@ -364,16 +364,16 @@ public class TestNumberParser extends AbstractTestPrinterParser { {"0", 1, 2, SignStyle.NORMAL, 1, 0}, {"5", 1, 2, SignStyle.NORMAL, 1, 5}, {"50", 1, 2, SignStyle.NORMAL, 2, 50}, - {"500", 1, 2, SignStyle.NORMAL, 2, 50}, + {"500", 1, 2, SignStyle.NORMAL, 3, 500}, {"-0", 1, 2, SignStyle.NORMAL, 2, 0}, {"-5", 1, 2, SignStyle.NORMAL, 2, -5}, {"-50", 1, 2, SignStyle.NORMAL, 3, -50}, - {"-500", 1, 2, SignStyle.NORMAL, 3, -50}, + {"-500", 1, 2, SignStyle.NORMAL, 4, -500}, {"-AAA", 1, 2, SignStyle.NORMAL, 1, null}, {"+0", 1, 2, SignStyle.NORMAL, 2, 0}, {"+5", 1, 2, SignStyle.NORMAL, 2, 5}, {"+50", 1, 2, SignStyle.NORMAL, 3, 50}, - {"+500", 1, 2, SignStyle.NORMAL, 3, 50}, + {"+500", 1, 2, SignStyle.NORMAL, 4, 500}, {"+AAA", 1, 2, SignStyle.NORMAL, 1, null}, {"50", 2, 2, SignStyle.NORMAL, 2, 50}, {"-50", 2, 2, SignStyle.NORMAL, 3, -50}, @@ -383,32 +383,32 @@ public class TestNumberParser extends AbstractTestPrinterParser { {"0", 1, 2, SignStyle.ALWAYS, 1, 0}, {"5", 1, 2, SignStyle.ALWAYS, 1, 5}, {"50", 1, 2, SignStyle.ALWAYS, 2, 50}, - {"500", 1, 2, SignStyle.ALWAYS, 2, 50}, + {"500", 1, 2, SignStyle.ALWAYS, 3, 500}, {"-0", 1, 2, SignStyle.ALWAYS, 2, 0}, {"-5", 1, 2, SignStyle.ALWAYS, 2, -5}, {"-50", 1, 2, SignStyle.ALWAYS, 3, -50}, - {"-500", 1, 2, SignStyle.ALWAYS, 3, -50}, + {"-500", 1, 2, SignStyle.ALWAYS, 4, -500}, {"-AAA", 1, 2, SignStyle.ALWAYS, 1, null}, {"+0", 1, 2, SignStyle.ALWAYS, 2, 0}, {"+5", 1, 2, SignStyle.ALWAYS, 2, 5}, {"+50", 1, 2, SignStyle.ALWAYS, 3, 50}, - {"+500", 1, 2, SignStyle.ALWAYS, 3, 50}, + {"+500", 1, 2, SignStyle.ALWAYS, 4, 500}, {"+AAA", 1, 2, SignStyle.ALWAYS, 1, null}, // exceeds pad {"0", 1, 2, SignStyle.EXCEEDS_PAD, 1, 0}, {"5", 1, 2, SignStyle.EXCEEDS_PAD, 1, 5}, {"50", 1, 2, SignStyle.EXCEEDS_PAD, 2, 50}, - {"500", 1, 2, SignStyle.EXCEEDS_PAD, 2, 50}, + {"500", 1, 2, SignStyle.EXCEEDS_PAD, 3, 500}, {"-0", 1, 2, SignStyle.EXCEEDS_PAD, 2, 0}, {"-5", 1, 2, SignStyle.EXCEEDS_PAD, 2, -5}, {"-50", 1, 2, SignStyle.EXCEEDS_PAD, 3, -50}, - {"-500", 1, 2, SignStyle.EXCEEDS_PAD, 3, -50}, + {"-500", 1, 2, SignStyle.EXCEEDS_PAD, 4, -500}, {"-AAA", 1, 2, SignStyle.EXCEEDS_PAD, 1, null}, {"+0", 1, 2, SignStyle.EXCEEDS_PAD, 2, 0}, {"+5", 1, 2, SignStyle.EXCEEDS_PAD, 2, 5}, {"+50", 1, 2, SignStyle.EXCEEDS_PAD, 3, 50}, - {"+500", 1, 2, SignStyle.EXCEEDS_PAD, 3, 50}, + {"+500", 1, 2, SignStyle.EXCEEDS_PAD, 4, 500}, {"+AAA", 1, 2, SignStyle.EXCEEDS_PAD, 1, null}, }; } @@ -441,9 +441,9 @@ public class TestNumberParser extends AbstractTestPrinterParser { {"543", 1, 3, SignStyle.NEVER, 3, 543}, {"543", 2, 3, SignStyle.NEVER, 3, 543}, {"543", 3, 3, SignStyle.NEVER, 3, 543}, - {"5432", 1, 3, SignStyle.NEVER, 3, 543}, - {"5432", 2, 3, SignStyle.NEVER, 3, 543}, - {"5432", 3, 3, SignStyle.NEVER, 3, 543}, + {"5432", 1, 3, SignStyle.NEVER, 4, 5432}, + {"5432", 2, 3, SignStyle.NEVER, 4, 5432}, + {"5432", 3, 3, SignStyle.NEVER, 4, 5432}, {"5AAA", 2, 3, SignStyle.NEVER, 1, 5}, // not negative @@ -455,9 +455,9 @@ public class TestNumberParser extends AbstractTestPrinterParser { {"543", 1, 3, SignStyle.NOT_NEGATIVE, 3, 543}, {"543", 2, 3, SignStyle.NOT_NEGATIVE, 3, 543}, {"543", 3, 3, SignStyle.NOT_NEGATIVE, 3, 543}, - {"5432", 1, 3, SignStyle.NOT_NEGATIVE, 3, 543}, - {"5432", 2, 3, SignStyle.NOT_NEGATIVE, 3, 543}, - {"5432", 3, 3, SignStyle.NOT_NEGATIVE, 3, 543}, + {"5432", 1, 3, SignStyle.NOT_NEGATIVE, 4, 5432}, + {"5432", 2, 3, SignStyle.NOT_NEGATIVE, 4, 5432}, + {"5432", 3, 3, SignStyle.NOT_NEGATIVE, 4, 5432}, {"5AAA", 2, 3, SignStyle.NOT_NEGATIVE, 1, 5}, // normal @@ -469,9 +469,9 @@ public class TestNumberParser extends AbstractTestPrinterParser { {"543", 1, 3, SignStyle.NORMAL, 3, 543}, {"543", 2, 3, SignStyle.NORMAL, 3, 543}, {"543", 3, 3, SignStyle.NORMAL, 3, 543}, - {"5432", 1, 3, SignStyle.NORMAL, 3, 543}, - {"5432", 2, 3, SignStyle.NORMAL, 3, 543}, - {"5432", 3, 3, SignStyle.NORMAL, 3, 543}, + {"5432", 1, 3, SignStyle.NORMAL, 4, 5432}, + {"5432", 2, 3, SignStyle.NORMAL, 4, 5432}, + {"5432", 3, 3, SignStyle.NORMAL, 4, 5432}, {"5AAA", 2, 3, SignStyle.NORMAL, 1, 5}, // always @@ -483,9 +483,9 @@ public class TestNumberParser extends AbstractTestPrinterParser { {"543", 1, 3, SignStyle.ALWAYS, 3, 543}, {"543", 2, 3, SignStyle.ALWAYS, 3, 543}, {"543", 3, 3, SignStyle.ALWAYS, 3, 543}, - {"5432", 1, 3, SignStyle.ALWAYS, 3, 543}, - {"5432", 2, 3, SignStyle.ALWAYS, 3, 543}, - {"5432", 3, 3, SignStyle.ALWAYS, 3, 543}, + {"5432", 1, 3, SignStyle.ALWAYS, 4, 5432}, + {"5432", 2, 3, SignStyle.ALWAYS, 4, 5432}, + {"5432", 3, 3, SignStyle.ALWAYS, 4, 5432}, {"5AAA", 2, 3, SignStyle.ALWAYS, 1, 5}, // exceeds pad @@ -497,9 +497,9 @@ public class TestNumberParser extends AbstractTestPrinterParser { {"543", 1, 3, SignStyle.EXCEEDS_PAD, 3, 543}, {"543", 2, 3, SignStyle.EXCEEDS_PAD, 3, 543}, {"543", 3, 3, SignStyle.EXCEEDS_PAD, 3, 543}, - {"5432", 1, 3, SignStyle.EXCEEDS_PAD, 3, 543}, - {"5432", 2, 3, SignStyle.EXCEEDS_PAD, 3, 543}, - {"5432", 3, 3, SignStyle.EXCEEDS_PAD, 3, 543}, + {"5432", 1, 3, SignStyle.EXCEEDS_PAD, 4, 5432}, + {"5432", 2, 3, SignStyle.EXCEEDS_PAD, 4, 5432}, + {"5432", 3, 3, SignStyle.EXCEEDS_PAD, 4, 5432}, {"5AAA", 2, 3, SignStyle.EXCEEDS_PAD, 1, 5}, }; } @@ -533,8 +533,8 @@ public class TestNumberParser extends AbstractTestPrinterParser { {"5432", 4, 54, 32}, {"5432A", 4, 54, 32}, - {"54321", 4, 54, 32}, - {"54321A", 4, 54, 32}, + {"54321", 5, 543, 21}, + {"54321A", 5, 543, 21}, }; } @@ -544,7 +544,7 @@ public class TestNumberParser extends AbstractTestPrinterParser { ParsePosition pos = new ParsePosition(0); DateTimeFormatter f = builder .appendValue(MONTH_OF_YEAR, 1, 2, SignStyle.NORMAL) - .appendValue(DAY_OF_MONTH, 2).toFormatter(locale).withSymbols(symbols); + .appendValue(DAY_OF_MONTH, 2).toFormatter(locale).withDecimalStyle(decimalStyle); TemporalAccessor parsed = f.parseUnresolved(input, pos); if (pos.getErrorIndex() != -1) { assertEquals(pos.getErrorIndex(), parseLen); diff --git a/jdk/test/java/time/test/java/time/format/TestReducedParser.java b/jdk/test/java/time/test/java/time/format/TestReducedParser.java index b8ae45f8542..237b5b9416d 100644 --- a/jdk/test/java/time/test/java/time/format/TestReducedParser.java +++ b/jdk/test/java/time/test/java/time/format/TestReducedParser.java @@ -59,13 +59,18 @@ */ package test.java.time.format; +import static java.time.temporal.ChronoField.DAY_OF_MONTH; import static java.time.temporal.ChronoField.DAY_OF_YEAR; +import static java.time.temporal.ChronoField.MONTH_OF_YEAR; import static java.time.temporal.ChronoField.YEAR; +import static java.time.temporal.ChronoField.YEAR_OF_ERA; import static org.testng.Assert.assertEquals; import static org.testng.Assert.assertTrue; +import static org.testng.Assert.assertNotNull; import java.text.ParsePosition; import java.time.format.DateTimeFormatter; +import java.time.format.DateTimeFormatterBuilder; import java.time.temporal.TemporalAccessor; import java.time.temporal.TemporalField; @@ -77,9 +82,15 @@ import org.testng.annotations.Test; */ @Test public class TestReducedParser extends AbstractTestPrinterParser { + private static final boolean STRICT = true; + private static final boolean LENIENT = false; private DateTimeFormatter getFormatter0(TemporalField field, int width, int baseValue) { - return builder.appendValueReduced(field, width, baseValue).toFormatter(locale).withSymbols(symbols); + return builder.appendValueReduced(field, width, baseValue).toFormatter(locale).withDecimalStyle(decimalStyle); + } + + private DateTimeFormatter getFormatter0(TemporalField field, int minWidth, int maxWidth, int baseValue) { + return builder.appendValueReduced(field, minWidth, maxWidth, baseValue).toFormatter(locale).withDecimalStyle(decimalStyle); } //----------------------------------------------------------------------- @@ -109,89 +120,242 @@ public class TestReducedParser extends AbstractTestPrinterParser { } //----------------------------------------------------------------------- - @DataProvider(name="Parse") - Object[][] provider_parse() { + // Parse data and values that are consistent whether strict or lenient + // The data is the ChronoField, width, baseValue, text, startPos, endPos, value + //----------------------------------------------------------------------- + @DataProvider(name="ParseAll") + Object[][] provider_parseAll() { return new Object[][] { // negative zero {YEAR, 1, 2010, "-0", 0, 0, null}, // general {YEAR, 2, 2010, "Xxx12Xxx", 3, 5, 2012}, - {YEAR, 2, 2010, "12345", 0, 2, 2012}, {YEAR, 2, 2010, "12-45", 0, 2, 2012}, - // insufficient digits - {YEAR, 2, 2010, "0", 0, 0, null}, - {YEAR, 2, 2010, "1", 0, 0, null}, - {YEAR, 2, 2010, "1", 1, 1, null}, - {YEAR, 2, 2010, "1-2", 0, 0, null}, - {YEAR, 2, 2010, "9", 0, 0, null}, - // other junk {YEAR, 2, 2010, "A0", 0, 0, null}, - {YEAR, 2, 2010, "0A", 0, 0, null}, {YEAR, 2, 2010, " 1", 0, 0, null}, {YEAR, 2, 2010, "-1", 0, 0, null}, {YEAR, 2, 2010, "-10", 0, 0, null}, + {YEAR, 2, 2000, " 1", 0, 0, null}, // parse OK 1 - {YEAR, 1, 2010, "0", 0, 1, 2010}, + {YEAR, 1, 2010, "1", 0, 1, 2011}, + {YEAR, 1, 2010, "3", 1, 1, null}, {YEAR, 1, 2010, "9", 0, 1, 2019}, - {YEAR, 1, 2010, "10", 0, 1, 2011}, {YEAR, 1, 2005, "0", 0, 1, 2010}, {YEAR, 1, 2005, "4", 0, 1, 2014}, {YEAR, 1, 2005, "5", 0, 1, 2005}, {YEAR, 1, 2005, "9", 0, 1, 2009}, - {YEAR, 1, 2005, "10", 0, 1, 2011}, + {YEAR, 1, 2010, "1-2", 0, 1, 2011}, // parse OK 2 {YEAR, 2, 2010, "00", 0, 2, 2100}, {YEAR, 2, 2010, "09", 0, 2, 2109}, {YEAR, 2, 2010, "10", 0, 2, 2010}, {YEAR, 2, 2010, "99", 0, 2, 2099}, - {YEAR, 2, 2010, "100", 0, 2, 2010}, // parse OK 2 {YEAR, 2, -2005, "05", 0, 2, -2005}, {YEAR, 2, -2005, "00", 0, 2, -2000}, {YEAR, 2, -2005, "99", 0, 2, -1999}, {YEAR, 2, -2005, "06", 0, 2, -1906}, - {YEAR, 2, -2005, "100", 0, 2, -1910}, - }; + + {YEAR, 2, -2005, "43", 0, 2, -1943}, + }; } - @Test(dataProvider="Parse") - public void test_parse(TemporalField field, int width, int baseValue, String input, int pos, int parseLen, Integer parseVal) { + @Test(dataProvider="ParseAll") + public void test_parseAllStrict(TemporalField field, int width, int baseValue, String input, int pos, int parseLen, Integer parseVal) { ParsePosition ppos = new ParsePosition(pos); + setStrict(true); TemporalAccessor parsed = getFormatter0(field, width, baseValue).parseUnresolved(input, ppos); if (ppos.getErrorIndex() != -1) { - assertEquals(ppos.getErrorIndex(), parseLen); + assertEquals(ppos.getErrorIndex(), parseLen, "error case parse position"); + assertEquals(parsed, parseVal, "unexpected parse result"); } else { - assertEquals(ppos.getIndex(), parseLen); + assertEquals(ppos.getIndex(), parseLen, "parse position"); assertParsed(parsed, YEAR, parseVal != null ? (long) parseVal : null); } } - @Test(dataProvider="Parse") - public void test_parseLenient(TemporalField field, int width, int baseValue, String input, int pos, int parseLen, Integer parseVal) { - setStrict(false); + @Test(dataProvider="ParseAll") + public void test_parseAllLenient(TemporalField field, int width, int baseValue, String input, int pos, int parseLen, Integer parseVal) { ParsePosition ppos = new ParsePosition(pos); + setStrict(false); TemporalAccessor parsed = getFormatter0(field, width, baseValue).parseUnresolved(input, ppos); if (ppos.getErrorIndex() != -1) { - assertEquals(ppos.getErrorIndex(), parseLen); + assertEquals(ppos.getErrorIndex(), parseLen, "error case parse position"); + assertEquals(parsed, parseVal, "unexpected parse result"); } else { - assertEquals(ppos.getIndex(), parseLen); + assertEquals(ppos.getIndex(), parseLen, "parse position"); assertParsed(parsed, YEAR, parseVal != null ? (long) parseVal : null); } } + //----------------------------------------------------------------------- + // Parse data and values in strict and lenient modes. + // The data is the ChronoField, minWidth, maxWidth, baseValue, text, startPos, + // Strict Pair(endPos, value), Lenient Pair(endPos, value) + //----------------------------------------------------------------------- + @DataProvider(name="ParseLenientSensitive") + Object[][] provider_parseLenientSensitive() { + return new Object[][] { + // few digits supplied + {YEAR, 2, 2, 2010, "3", 0, strict(0, null), lenient(1, 3)}, + {YEAR, 2, 2, 2010, "4", 0, strict(0, null), lenient(1, 4)}, + {YEAR, 2, 2, 2010, "5", 1, strict(1, null), lenient(1, null)}, + {YEAR, 2, 2, 2010, "6-2", 0, strict(0, null), lenient(1, 6)}, + {YEAR, 2, 2, 2010, "9", 0, strict(0, null), lenient(1, 9)}, + + // other junk + {YEAR, 1, 4, 2000, "7A", 0, strict(1, 2007), lenient(1, 2007)}, + {YEAR, 2, 2, 2010, "8A", 0, strict(0, null), lenient(1, 8)}, + + // Negative sign cases + {YEAR, 2, 4, 2000, "-1", 0, strict(0, null), lenient(2, -1)}, + {YEAR, 2, 4, 2000, "-10", 0, strict(0, null), lenient(3, -10)}, + + // Positive sign cases + {YEAR, 2, 4, 2000, "+1", 0, strict(0, null), lenient(2, 1)}, + {YEAR, 2, 4, 2000, "+10", 0, strict(0, null), lenient(3, 2010)}, + + // No sign cases + {YEAR, 1, 1, 2005, "21", 0, strict(1, 2012), lenient(2, 21)}, + {YEAR, 1, 2, 2010, "12", 0, strict(2, 12), lenient(2, 12)}, + {YEAR, 1, 4, 2000, "87", 0, strict(2, 87), lenient(2, 87)}, + {YEAR, 1, 4, 2000, "9876", 0, strict(4, 9876), lenient(4, 9876)}, + {YEAR, 2, 2, 2010, "321", 0, strict(2, 2032), lenient(3, 321)}, + {YEAR, 2, 4, 2010, "2", 0, strict(0, null), lenient(1, 2)}, + {YEAR, 2, 4, 2010, "21", 0, strict(2, 2021), lenient(2, 2021)}, + {YEAR, 2, 4, 2010, "321", 0, strict(3, 321), lenient(3, 321)}, + {YEAR, 2, 4, 2010, "4321", 0, strict(4, 4321), lenient(4, 4321)}, + {YEAR, 2, 4, 2010, "54321", 0, strict(4, 5432), lenient(5, 54321)}, + {YEAR, 2, 8, 2010, "87654321", 3, strict(8, 54321), lenient(8, 54321)}, + {YEAR, 2, 9, 2010, "987654321", 0, strict(9, 987654321), lenient(9, 987654321)}, + {YEAR, 3, 3, 2010, "765", 0, strict(3, 2765), lenient(3, 2765)}, + {YEAR, 3, 4, 2010, "76", 0, strict(0, null), lenient(2, 76)}, + {YEAR, 3, 4, 2010, "765", 0, strict(3, 2765), lenient(3, 2765)}, + {YEAR, 3, 4, 2010, "7654", 0, strict(4, 7654), lenient(4, 7654)}, + {YEAR, 3, 4, 2010, "76543", 0, strict(4, 7654), lenient(5, 76543)}, + + // Negative baseValue + {YEAR, 2, 4, -2005, "123", 0, strict(3, 123), lenient(3, 123)}, + }; + } + + //----------------------------------------------------------------------- + // Parsing tests for strict mode + //----------------------------------------------------------------------- + @Test(dataProvider="ParseLenientSensitive") + public void test_parseStrict(TemporalField field, int minWidth, int maxWidth, int baseValue, String input, int pos, + Pair strict, Pair lenient) { + ParsePosition ppos = new ParsePosition(pos); + setStrict(true); + TemporalAccessor parsed = getFormatter0(field, minWidth, maxWidth, baseValue).parseUnresolved(input, ppos); + if (ppos.getErrorIndex() != -1) { + assertEquals(ppos.getErrorIndex(), strict.parseLen, "error case parse position"); + assertEquals(parsed, strict.parseVal, "unexpected parse result"); + } else { + assertEquals(ppos.getIndex(), strict.parseLen, "parse position"); + assertParsed(parsed, YEAR, strict.parseVal != null ? (long) strict.parseVal : null); + } + } + + //----------------------------------------------------------------------- + // Parsing tests for lenient mode + //----------------------------------------------------------------------- + @Test(dataProvider="ParseLenientSensitive") + public void test_parseLenient(TemporalField field, int minWidth, int maxWidth, int baseValue, String input, int pos, + Pair strict, Pair lenient) { + ParsePosition ppos = new ParsePosition(pos); + setStrict(false); + TemporalAccessor parsed = getFormatter0(field, minWidth, maxWidth, baseValue).parseUnresolved(input, ppos); + if (ppos.getErrorIndex() != -1) { + assertEquals(ppos.getErrorIndex(), lenient.parseLen, "error case parse position"); + assertEquals(parsed, lenient.parseVal, "unexpected parse result"); + } else { + assertEquals(ppos.getIndex(), lenient.parseLen, "parse position"); + assertParsed(parsed, YEAR, lenient.parseVal != null ? (long) lenient.parseVal : null); + } + } + private void assertParsed(TemporalAccessor parsed, TemporalField field, Long value) { if (value == null) { - assertEquals(parsed, null); + assertEquals(parsed, null, "Parsed Value"); } else { - assertEquals(parsed.isSupported(field), true); - assertEquals(parsed.getLong(field), (long) value); + assertEquals(parsed.isSupported(field), true, "isSupported: " + field); + assertEquals(parsed.getLong(field), (long) value, "Temporal.getLong: " + field); + } + } + + + //----------------------------------------------------------------------- + // Cases and values in adjacent parsing mode + //----------------------------------------------------------------------- + @DataProvider(name="ParseAdjacent") + Object[][] provider_parseAdjacent() { + return new Object[][] { + // general + {"yyMMdd", "19990703", LENIENT, 0, 8, 1999, 7, 3}, + {"yyMMdd", "19990703", STRICT, 0, 6, 2019, 99, 7}, + {"yyMMdd", "990703", LENIENT, 0, 6, 2099, 7, 3}, + {"yyMMdd", "990703", STRICT, 0, 6, 2099, 7, 3}, + {"yyMMdd", "200703", LENIENT, 0, 6, 2020, 7, 3}, + {"yyMMdd", "200703", STRICT, 0, 6, 2020, 7, 3}, + {"ddMMyy", "230714", LENIENT, 0, 6, 2014, 7, 23}, + {"ddMMyy", "230714", STRICT, 0, 6, 2014, 7, 23}, + {"ddMMyy", "25062001", LENIENT, 0, 8, 2001, 20, 2506}, + {"ddMMyy", "25062001", STRICT, 0, 6, 2020, 6, 25}, + {"ddMMy", "27052002", LENIENT, 0, 8, 2002, 5, 27}, + {"ddMMy", "27052002", STRICT, 0, 8, 2002, 5, 27}, + }; + } + + @Test(dataProvider="ParseAdjacent") + public void test_parseAdjacent(String pattern, String input, boolean strict, int pos, int parseLen, int year, int month, int day) { + ParsePosition ppos = new ParsePosition(0); + builder = new DateTimeFormatterBuilder(); + setStrict(strict); + builder.appendPattern(pattern); + DateTimeFormatter dtf = builder.toFormatter(); + + TemporalAccessor parsed = dtf.parseUnresolved(input, ppos); + assertNotNull(parsed, String.format("parse failed: ppos: %s, formatter: %s%n", ppos.toString(), dtf)); + if (ppos.getErrorIndex() != -1) { + assertEquals(ppos.getErrorIndex(), parseLen, "error case parse position"); + } else { + assertEquals(ppos.getIndex(), parseLen, "parse position"); + assertParsed(parsed, YEAR_OF_ERA, Long.valueOf(year)); + assertParsed(parsed, MONTH_OF_YEAR, Long.valueOf(month)); + assertParsed(parsed, DAY_OF_MONTH, Long.valueOf(day)); + } + } + + //----------------------------------------------------------------------- + // Class to structure the test data + //----------------------------------------------------------------------- + + private static Pair strict(int parseLen, Integer parseVal) { + return new Pair(parseLen, parseVal, STRICT); + } + private static Pair lenient(int parseLen, Integer parseVal) { + return new Pair(parseLen, parseVal, LENIENT); + } + + private static class Pair { + public final int parseLen; + public final Integer parseVal; + private final boolean strict; + public Pair(int parseLen, Integer parseVal, boolean strict) { + this.parseLen = parseLen; + this.parseVal = parseVal; + this.strict = strict; + } + public String toString() { + return (strict ? "strict" : "lenient") + "(" + parseLen + "," + parseVal + ")"; } } diff --git a/jdk/test/java/time/test/java/time/format/TestReducedPrinter.java b/jdk/test/java/time/test/java/time/format/TestReducedPrinter.java index b213b80fece..5ff2cd5eed8 100644 --- a/jdk/test/java/time/test/java/time/format/TestReducedPrinter.java +++ b/jdk/test/java/time/test/java/time/format/TestReducedPrinter.java @@ -59,6 +59,7 @@ */ package test.java.time.format; +import java.text.ParsePosition; import static java.time.temporal.ChronoField.YEAR; import static org.testng.Assert.assertEquals; import static org.testng.Assert.fail; @@ -66,6 +67,11 @@ import static org.testng.Assert.fail; import java.time.DateTimeException; import java.time.LocalDate; import java.time.format.DateTimeFormatter; +import java.time.format.DateTimeFormatterBuilder; +import static java.time.temporal.ChronoField.DAY_OF_MONTH; +import static java.time.temporal.ChronoField.MONTH_OF_YEAR; +import static java.time.temporal.ChronoField.YEAR_OF_ERA; +import java.time.temporal.TemporalAccessor; import java.time.temporal.TemporalField; import org.testng.annotations.DataProvider; @@ -79,7 +85,11 @@ import test.java.time.temporal.MockFieldValue; public class TestReducedPrinter extends AbstractTestPrinterParser { private DateTimeFormatter getFormatter0(TemporalField field, int width, int baseValue) { - return builder.appendValueReduced(field, width, baseValue).toFormatter(locale).withSymbols(symbols); + return builder.appendValueReduced(field, width, baseValue).toFormatter(locale).withDecimalStyle(decimalStyle); + } + + private DateTimeFormatter getFormatter0(TemporalField field, int minWidth, int maxWidth, int baseValue) { + return builder.appendValueReduced(field, minWidth, maxWidth, baseValue).toFormatter(locale).withDecimalStyle(decimalStyle); } //----------------------------------------------------------------------- @@ -99,66 +109,76 @@ public class TestReducedPrinter extends AbstractTestPrinterParser { @DataProvider(name="Pivot") Object[][] provider_pivot() { return new Object[][] { - {1, 2010, 2010, "0"}, - {1, 2010, 2011, "1"}, - {1, 2010, 2012, "2"}, - {1, 2010, 2013, "3"}, - {1, 2010, 2014, "4"}, - {1, 2010, 2015, "5"}, - {1, 2010, 2016, "6"}, - {1, 2010, 2017, "7"}, - {1, 2010, 2018, "8"}, - {1, 2010, 2019, "9"}, - {1, 2010, 2009, "9"}, - {1, 2010, 2020, "0"}, + {1, 1, 2010, 2010, "0"}, + {1, 1, 2010, 2011, "1"}, + {1, 1, 2010, 2012, "2"}, + {1, 1, 2010, 2013, "3"}, + {1, 1, 2010, 2014, "4"}, + {1, 1, 2010, 2015, "5"}, + {1, 1, 2010, 2016, "6"}, + {1, 1, 2010, 2017, "7"}, + {1, 1, 2010, 2018, "8"}, + {1, 1, 2010, 2019, "9"}, + {1, 1, 2010, 2009, "9"}, + {1, 1, 2010, 2020, "0"}, - {2, 2010, 2010, "10"}, - {2, 2010, 2011, "11"}, - {2, 2010, 2021, "21"}, - {2, 2010, 2099, "99"}, - {2, 2010, 2100, "00"}, - {2, 2010, 2109, "09"}, - {2, 2010, 2009, "09"}, - {2, 2010, 2110, "10"}, + {2, 2, 2010, 2010, "10"}, + {2, 2, 2010, 2011, "11"}, + {2, 2, 2010, 2021, "21"}, + {2, 2, 2010, 2099, "99"}, + {2, 2, 2010, 2100, "00"}, + {2, 2, 2010, 2109, "09"}, + {2, 2, 2010, 2009, "09"}, + {2, 2, 2010, 2110, "10"}, - {2, 2005, 2005, "05"}, - {2, 2005, 2099, "99"}, - {2, 2005, 2100, "00"}, - {2, 2005, 2104, "04"}, - {2, 2005, 2004, "04"}, - {2, 2005, 2105, "05"}, + {2, 2, 2005, 2005, "05"}, + {2, 2, 2005, 2099, "99"}, + {2, 2, 2005, 2100, "00"}, + {2, 2, 2005, 2104, "04"}, + {2, 2, 2005, 2004, "04"}, + {2, 2, 2005, 2105, "05"}, - {3, 2005, 2005, "005"}, - {3, 2005, 2099, "099"}, - {3, 2005, 2100, "100"}, - {3, 2005, 2999, "999"}, - {3, 2005, 3000, "000"}, - {3, 2005, 3004, "004"}, - {3, 2005, 2004, "004"}, - {3, 2005, 3005, "005"}, + {3, 3, 2005, 2005, "005"}, + {3, 3, 2005, 2099, "099"}, + {3, 3, 2005, 2100, "100"}, + {3, 3, 2005, 2999, "999"}, + {3, 3, 2005, 3000, "000"}, + {3, 3, 2005, 3004, "004"}, + {3, 3, 2005, 2004, "004"}, + {3, 3, 2005, 3005, "005"}, - {9, 2005, 2005, "000002005"}, - {9, 2005, 2099, "000002099"}, - {9, 2005, 2100, "000002100"}, - {9, 2005, 999999999, "999999999"}, - {9, 2005, 1000000000, "000000000"}, - {9, 2005, 1000002004, "000002004"}, - {9, 2005, 2004, "000002004"}, - {9, 2005, 1000002005, "000002005"}, + {9, 9, 2005, 2005, "000002005"}, + {9, 9, 2005, 2099, "000002099"}, + {9, 9, 2005, 2100, "000002100"}, + {9, 9, 2005, 999999999, "999999999"}, + {9, 9, 2005, 1000000000, "000000000"}, + {9, 9, 2005, 1000002004, "000002004"}, + {9, 9, 2005, 2004, "000002004"}, + {9, 9, 2005, 1000002005, "000002005"}, - {2, -2005, -2005, "05"}, - {2, -2005, -2000, "00"}, - {2, -2005, -1999, "99"}, - {2, -2005, -1904, "04"}, - {2, -2005, -2006, "06"}, - {2, -2005, -1905, "05"}, - }; + {2, 2, -2005, -2005, "05"}, + {2, 2, -2005, -2000, "00"}, + {2, 2, -2005, -1999, "99"}, + {2, 2, -2005, -1904, "04"}, + {2, 2, -2005, -2006, "06"}, + {2, 2, -2005, -1905, "05"}, + + {2, 4, 2005, 2099, "99"}, + {2, 4, 2005, 2100, "00"}, + {2, 4, 2005, 9999, "9999"}, + {2, 4, 2005, 1000000000, "00"}, + {2, 4, 2005, 1000002004, "2004"}, + {2, 4, 2005, 2004, "2004"}, + {2, 4, 2005, 2005, "05"}, + {2, 4, 2005, 2104, "04"}, + {2, 4, 2005, 2105, "2105"}, + }; } @Test(dataProvider="Pivot") - public void test_pivot(int width, int baseValue, int value, String result) throws Exception { + public void test_pivot(int minWidth, int maxWidth, int baseValue, int value, String result) throws Exception { try { - getFormatter0(YEAR, width, baseValue).formatTo(new MockFieldValue(YEAR, value), buf); + getFormatter0(YEAR, minWidth, maxWidth, baseValue).formatTo(new MockFieldValue(YEAR, value), buf); if (result == null) { fail("Expected exception"); } @@ -174,7 +194,34 @@ public class TestReducedPrinter extends AbstractTestPrinterParser { //----------------------------------------------------------------------- public void test_toString() throws Exception { - assertEquals(getFormatter0(YEAR, 2, 2005).toString(), "ReducedValue(Year,2,2005)"); + assertEquals(getFormatter0(YEAR, 2, 2, 2005).toString(), "ReducedValue(Year,2,2,2005)"); + } + + //----------------------------------------------------------------------- + // Cases and values in adjacent parsing mode + //----------------------------------------------------------------------- + @DataProvider(name="PrintAdjacent") + Object[][] provider_printAdjacent() { + return new Object[][] { + // general + {"yyMMdd", "990703", 1999, 7, 3}, + {"yyMMdd", "990703", 2099, 7, 3}, + {"yyMMdd", "200703", 2020, 7, 3}, + {"ddMMyy", "030714", 2014, 7, 3}, + {"ddMMyy", "030720", 2020, 7, 3}, + {"ddMMy", "03072001", 2001, 7, 3}, + }; + } + + @Test(dataProvider="PrintAdjacent") + public void test_printAdjacent(String pattern, String text, int year, int month, int day) { + builder = new DateTimeFormatterBuilder(); + builder.appendPattern(pattern); + DateTimeFormatter dtf = builder.toFormatter(); + + LocalDate ld = LocalDate.of(year, month, day); + String actual = dtf.format(ld); + assertEquals(actual, text, "formatter output: " + dtf); } } diff --git a/jdk/test/java/time/test/java/time/format/TestZoneTextPrinterParser.java b/jdk/test/java/time/test/java/time/format/TestZoneTextPrinterParser.java index 0175c223826..7ab8804a9ad 100644 --- a/jdk/test/java/time/test/java/time/format/TestZoneTextPrinterParser.java +++ b/jdk/test/java/time/test/java/time/format/TestZoneTextPrinterParser.java @@ -28,7 +28,7 @@ import static org.testng.Assert.assertEquals; import java.text.DateFormatSymbols; import java.time.ZoneId; import java.time.ZonedDateTime; -import java.time.format.DateTimeFormatSymbols; +import java.time.format.DecimalStyle; import java.time.format.DateTimeFormatter; import java.time.format.DateTimeFormatterBuilder; import java.time.format.TextStyle; @@ -55,7 +55,7 @@ public class TestZoneTextPrinterParser extends AbstractTestPrinterParser { protected static DateTimeFormatter getFormatter(Locale locale, TextStyle style) { return new DateTimeFormatterBuilder().appendZoneText(style) .toFormatter(locale) - .withSymbols(DateTimeFormatSymbols.of(locale)); + .withDecimalStyle(DecimalStyle.of(locale)); } public void test_printText() { @@ -148,7 +148,7 @@ public class TestZoneTextPrinterParser extends AbstractTestPrinterParser { public void test_ParseText(String expected, String text, Set preferred, Locale locale, TextStyle style) { DateTimeFormatter fmt = new DateTimeFormatterBuilder().appendZoneText(style, preferred) .toFormatter(locale) - .withSymbols(DateTimeFormatSymbols.of(locale)); + .withDecimalStyle(DecimalStyle.of(locale)); String ret = fmt.parse(text, TemporalQuery.zone()).getId(); @@ -209,7 +209,7 @@ public class TestZoneTextPrinterParser extends AbstractTestPrinterParser { } return db.appendZoneText(style) .toFormatter(locale) - .withSymbols(DateTimeFormatSymbols.of(locale)); + .withDecimalStyle(DecimalStyle.of(locale)); } } diff --git a/jdk/test/java/util/Base64/TestBase64.java b/jdk/test/java/util/Base64/TestBase64.java index 94413a2aa60..a8895a0cc81 100644 --- a/jdk/test/java/util/Base64/TestBase64.java +++ b/jdk/test/java/util/Base64/TestBase64.java @@ -23,6 +23,7 @@ /** * @test 4235519 8004212 8005394 8007298 8006295 8006315 8006530 8007379 8008925 + * 8014217 * @summary tests java.util.Base64 */ @@ -110,6 +111,14 @@ public class TestBase64 { // illegal ending unit checkIAE(new Runnable() { public void run() { Base64.getMimeDecoder().decode("$=#"); }}); + checkIOE(new Testable() { public void test() throws IOException { + byte[] bytes = "AA=".getBytes("ASCII"); + try (InputStream stream = + Base64.getDecoder().wrap(new ByteArrayInputStream(bytes))) { + while (stream.read() != -1); + } + }}); + // test return value from decode(ByteBuffer, ByteBuffer) testDecBufRet(); diff --git a/jdk/test/java/util/Locale/LocaleProviders.java b/jdk/test/java/util/Locale/LocaleProviders.java index 97a5c2a7dbb..e5324e4dd91 100644 --- a/jdk/test/java/util/Locale/LocaleProviders.java +++ b/jdk/test/java/util/Locale/LocaleProviders.java @@ -118,27 +118,67 @@ public class LocaleProviders { if (System.getProperty("os.name").startsWith("Windows")) { NumberFormat nf = NumberFormat.getInstance(Locale.US); try { - double ver = nf.parse(System.getProperty("os.version")).doubleValue(); + double ver = nf.parse(System.getProperty("os.version")) + .doubleValue(); System.out.printf("Windows version: %.1f\n", ver); if (ver >= 6.0) { - LocaleProviderAdapter lda = LocaleProviderAdapter.getAdapter(LocaleNameProvider.class, Locale.ENGLISH); + LocaleProviderAdapter lda = + LocaleProviderAdapter.getAdapter( + LocaleNameProvider.class, Locale.ENGLISH); LocaleProviderAdapter.Type type = lda.getAdapterType(); if (type == LocaleProviderAdapter.Type.HOST) { + LocaleNameProvider lnp = lda.getLocaleNameProvider(); Locale mkmk = Locale.forLanguageTag("mk-MK"); String result = mkmk.getDisplayLanguage(Locale.ENGLISH); - if (!"Macedonian (FYROM)".equals(result)) { - throw new RuntimeException("Windows locale name provider did not return expected localized language name for \"mk\". Returned name was \"" + result + "\""); + String hostResult = + lnp.getDisplayLanguage(mkmk.getLanguage(), + Locale.ENGLISH); + System.out.printf(" Display language name for" + + " (mk_MK): result(HOST): \"%s\", returned: \"%s\"\n", + hostResult, result); + if (result == null || + hostResult != null && + !result.equals(hostResult)) { + throw new RuntimeException("Display language name" + + " mismatch for \"mk\". Returned name was" + + " \"" + result + "\", result(HOST): \"" + + hostResult + "\""); } result = Locale.US.getDisplayLanguage(Locale.ENGLISH); - if (!"English".equals(result)) { - throw new RuntimeException("Windows locale name provider did not return expected localized language name for \"en\". Returned name was \"" + result + "\""); + hostResult = + lnp.getDisplayLanguage(Locale.US.getLanguage(), + Locale.ENGLISH); + System.out.printf(" Display language name for" + + " (en_US): result(HOST): \"%s\", returned: \"%s\"\n", + hostResult, result); + if (result == null || + hostResult != null && + !result.equals(hostResult)) { + throw new RuntimeException("Display language name" + + " mismatch for \"en\". Returned name was" + + " \"" + result + "\", result(HOST): \"" + + hostResult + "\""); } - result = Locale.US.getDisplayCountry(Locale.ENGLISH); - if (ver >= 6.1 && !"United States".equals(result)) { - throw new RuntimeException("Windows locale name provider did not return expected localized country name for \"US\". Returned name was \"" + result + "\""); + if (ver >= 6.1) { + result = Locale.US.getDisplayCountry(Locale.ENGLISH); + hostResult = lnp.getDisplayCountry( + Locale.US.getCountry(), Locale.ENGLISH); + System.out.printf(" Display country name for" + + " (en_US): result(HOST): \"%s\", returned: \"%s\"\n", + hostResult, result); + if (result == null || + hostResult != null && + !result.equals(hostResult)) { + throw new RuntimeException("Display country name" + + " mismatch for \"US\". Returned name was" + + " \"" + result + "\", result(HOST): \"" + + hostResult + "\""); + } } } else { - throw new RuntimeException("Windows Host LocaleProviderAdapter was not selected for English locale."); + throw new RuntimeException("Windows Host" + + " LocaleProviderAdapter was not selected for" + + " English locale."); } } } catch (ParseException pe) { diff --git a/jdk/test/java/util/Locale/LocaleProviders.sh b/jdk/test/java/util/Locale/LocaleProviders.sh index 3b1f96fd196..4d8bd06ff08 100644 --- a/jdk/test/java/util/Locale/LocaleProviders.sh +++ b/jdk/test/java/util/Locale/LocaleProviders.sh @@ -24,7 +24,7 @@ # # @test # @bug 6336885 7196799 7197573 7198834 8000245 8000615 8001440 8010666 -# 8013086 +# 8013086 8013233 # @summary tests for "java.locale.providers" system property # @compile -XDignore.symbol.file LocaleProviders.java # @run shell/timeout=600 LocaleProviders.sh diff --git a/jdk/test/java/util/Objects/BasicObjectsTest.java b/jdk/test/java/util/Objects/BasicObjectsTest.java index f87d0675a14..3a1cad9cd32 100644 --- a/jdk/test/java/util/Objects/BasicObjectsTest.java +++ b/jdk/test/java/util/Objects/BasicObjectsTest.java @@ -1,5 +1,5 @@ /* - * Copyright (c) 2009, 2011, Oracle and/or its affiliates. All rights reserved. + * Copyright (c) 2009, 2013, Oracle and/or its affiliates. All rights reserved. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * * This code is free software; you can redistribute it and/or modify it @@ -23,12 +23,13 @@ /* * @test - * @bug 6797535 6889858 6891113 + * @bug 6797535 6889858 6891113 8013712 8011800 8014365 * @summary Basic tests for methods in java.util.Objects * @author Joseph D. Darcy */ import java.util.*; +import java.util.function.*; public class BasicObjectsTest { public static void main(String... args) { @@ -162,47 +163,52 @@ public class BasicObjectsTest { private static int testRequireNonNull() { int errors = 0; - String s; - // Test 1-arg variant + final String RNN_1 = "1-arg requireNonNull"; + final String RNN_2 = "2-arg requireNonNull"; + final String RNN_3 = "Supplier requireNonNull"; + + Function rnn1 = s -> Objects.requireNonNull(s); + Function rnn2 = s -> Objects.requireNonNull(s, "trousers"); + Function rnn3 = s -> Objects.requireNonNull(s, () -> "trousers"); + + errors += testRNN_NonNull(rnn1, RNN_1); + errors += testRNN_NonNull(rnn2, RNN_2); + errors += testRNN_NonNull(rnn3, RNN_3); + + errors += testRNN_Null(rnn1, RNN_1, null); + errors += testRNN_Null(rnn2, RNN_2, "trousers"); + errors += testRNN_Null(rnn3, RNN_3, "trousers"); + return errors; + } + + private static int testRNN_NonNull(Function testFunc, + String testFuncName) { + int errors = 0; try { - s = Objects.requireNonNull("pants"); + String s = testFunc.apply("pants"); if (s != "pants") { - System.err.printf("1-arg non-null failed to return its arg"); + System.err.printf(testFuncName + " failed to return its arg"); errors++; } } catch (NullPointerException e) { - System.err.printf("1-arg nonNull threw unexpected NPE"); + System.err.printf(testFuncName + " threw unexpected NPE"); errors++; } + return errors; + } + private static int testRNN_Null(Function testFunc, + String testFuncName, + String expectedMessage) { + int errors = 0; try { - s = Objects.requireNonNull(null); - System.err.printf("1-arg nonNull failed to throw NPE"); + String s = testFunc.apply(null); + System.err.printf(testFuncName + " failed to throw NPE"); errors++; } catch (NullPointerException e) { - // Expected - } - - // Test 2-arg variant - try { - s = Objects.requireNonNull("pants", "trousers"); - if (s != "pants") { - System.err.printf("2-arg nonNull failed to return its arg"); - errors++; - } - } catch (NullPointerException e) { - System.err.printf("2-arg nonNull threw unexpected NPE"); - errors++; - } - - try { - s = Objects.requireNonNull(null, "pantaloons"); - System.err.printf("2-arg nonNull failed to throw NPE"); - errors++; - } catch (NullPointerException e) { - if (e.getMessage() != "pantaloons") { - System.err.printf("2-arg nonNull threw NPE w/ bad detail msg"); + if (e.getMessage() != expectedMessage) { + System.err.printf(testFuncName + " threw NPE w/ bad detail msg"); errors++; } } diff --git a/jdk/test/sun/util/calendar/zi/Rule.java b/jdk/test/sun/util/calendar/zi/Rule.java index 8e3a1189bb9..e8c79da9263 100644 --- a/jdk/test/sun/util/calendar/zi/Rule.java +++ b/jdk/test/sun/util/calendar/zi/Rule.java @@ -106,6 +106,7 @@ class Rule { final int y = year; RuleRec[] recs = new RuleRec[rules.size()]; rules.toArray(recs); + Arrays.sort(recs, new Comparator() { public int compare(RuleRec r1, RuleRec r2) { int n = r1.getMonthNum() - r2.getMonthNum(); @@ -117,7 +118,7 @@ class Rule { r1.getDay(), r1.getTime().getTime()); long t2 = Time.getLocalTime(y, r2.getMonth(), r2.getDay(), r2.getTime().getTime()); - return (int)(t1 - t2); + return Long.compare(t1, t2); } public boolean equals(Object o) { return this == o; diff --git a/jdk/test/sun/util/calendar/zi/tzdata/VERSION b/jdk/test/sun/util/calendar/zi/tzdata/VERSION index 85db871ccf3..8ad1064e058 100644 --- a/jdk/test/sun/util/calendar/zi/tzdata/VERSION +++ b/jdk/test/sun/util/calendar/zi/tzdata/VERSION @@ -21,4 +21,4 @@ # or visit www.oracle.com if you need additional information or have any # questions. # -tzdata2012i +tzdata2013c diff --git a/jdk/test/sun/util/calendar/zi/tzdata/africa b/jdk/test/sun/util/calendar/zi/tzdata/africa index 7db9b3d269d..2f5d3c5e3fc 100644 --- a/jdk/test/sun/util/calendar/zi/tzdata/africa +++ b/jdk/test/sun/util/calendar/zi/tzdata/africa @@ -27,9 +27,9 @@ # This data is by no means authoritative; if you think you know better, # go ahead and edit the file (and please send any changes to -# tz@elsie.nci.nih.gov for general use in the future). +# tz@iana.org for general use in the future). -# From Paul Eggert (2006-03-22): +# From Paul Eggert (2013-02-21): # # A good source for time zone historical data outside the U.S. is # Thomas G. Shanks and Rique Pottenger, The International Atlas (6th edition), @@ -48,6 +48,10 @@ # Whitman Publishing Co, 2 Niagara Av, Ealing, London (undated), which # I found in the UCLA library. # +# For data circa 1899, a common source is: +# Milne J. Civil time. Geogr J. 1899 Feb;13(2):173-94 +# . +# # A reliable and entertaining source about time zones is # Derek Howse, Greenwich time and longitude, Philip Wilson Publishers (1997). # @@ -139,8 +143,12 @@ Zone Africa/Porto-Novo 0:10:28 - LMT 1912 1:00 - WAT # Botswana +# From Paul Eggert (2013-02-21): +# Milne says they were regulated by the Cape Town Signal in 1899; +# assume they switched to 2:00 when Cape Town did. # Zone NAME GMTOFF RULES FORMAT [UNTIL] Zone Africa/Gaborone 1:43:40 - LMT 1885 + 1:30 - SAST 1903 Mar 2:00 - CAT 1943 Sep 19 2:00 2:00 1:00 CAST 1944 Mar 19 2:00 2:00 - CAT @@ -212,6 +220,11 @@ Zone Africa/Djibouti 2:52:36 - LMT 1911 Jul # Egypt +# Milne says Cairo used 2:05:08.9, the local mean time of the Abbasizeh +# observatory; round to nearest. Milne also says that the official time for +# Egypt was mean noon at the Great Pyramid, 2:04:30.5, but apparently this +# did not apply to Cairo, Alexandria, or Port Said. + # Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S Rule Egypt 1940 only - Jul 15 0:00 1:00 S Rule Egypt 1940 only - Oct 1 0:00 0 - @@ -352,7 +365,7 @@ Rule Egypt 2010 only - Sep 10 0:00 1:00 S Rule Egypt 2010 only - Sep lastThu 23:00s 0 - # Zone NAME GMTOFF RULES FORMAT [UNTIL] -Zone Africa/Cairo 2:05:00 - LMT 1900 Oct +Zone Africa/Cairo 2:05:09 - LMT 1900 Oct 2:00 Egypt EE%sT # Equatorial Guinea @@ -447,6 +460,20 @@ Zone Africa/Monrovia -0:43:08 - LMT 1882 # Libya +# From Even Scharning (2012-11-10): +# Libya set their time one hour back at 02:00 on Saturday November 10. +# http://www.libyaherald.com/2012/11/04/clocks-to-go-back-an-hour-on-saturday/ +# Here is an official source [in Arabic]: http://ls.ly/fb6Yc +# +# Steffen Thorsen forwarded a translation (2012-11-10) in +# http://mm.icann.org/pipermail/tz/2012-November/018451.html +# +# From Tim Parenti (2012-11-11): +# Treat the 2012-11-10 change as a zone change from UTC+2 to UTC+1. +# The DST rules planned for 2013 and onward roughly mirror those of Europe +# (either two days before them or five days after them, so as to fall on +# lastFri instead of lastSun). + # Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S Rule Libya 1951 only - Oct 14 2:00 1:00 S Rule Libya 1952 only - Jan 1 0:00 0 - @@ -461,17 +488,21 @@ Rule Libya 1986 only - Apr 4 0:00 1:00 S Rule Libya 1986 only - Oct 3 0:00 0 - Rule Libya 1987 1989 - Apr 1 0:00 1:00 S Rule Libya 1987 1989 - Oct 1 0:00 0 - +Rule Libya 1997 only - Apr 4 0:00 1:00 S +Rule Libya 1997 only - Oct 4 0:00 0 - +Rule Libya 2013 max - Mar lastFri 1:00 1:00 S +Rule Libya 2013 max - Oct lastFri 2:00 0 - # Zone NAME GMTOFF RULES FORMAT [UNTIL] Zone Africa/Tripoli 0:52:44 - LMT 1920 1:00 Libya CE%sT 1959 2:00 - EET 1982 1:00 Libya CE%sT 1990 May 4 -# The following entries are from Shanks & Pottenger; +# The 1996 and 1997 entries are from Shanks & Pottenger; # the IATA SSIM data contain some obvious errors. 2:00 - EET 1996 Sep 30 - 1:00 - CET 1997 Apr 4 - 1:00 1:00 CEST 1997 Oct 4 - 2:00 - EET + 1:00 Libya CE%sT 1997 Oct 4 + 2:00 - EET 2012 Nov 10 2:00 + 1:00 Libya CE%sT # Madagascar # Zone NAME GMTOFF RULES FORMAT [UNTIL] @@ -838,6 +869,41 @@ Zone Indian/Mayotte 3:00:56 - LMT 1911 Jul # Mamoutzou # 3:00 am Friday, July 20, 2012 and will again be advanced by 60 minutes # August 20, 2012 from 2:00 am. +# From Paul Eggert (2013-03-06): +# Morocco's daylight-saving transitions due to Ramadan seem to be +# announced a bit in advance. On 2012-07-11 the Moroccan government +# announced that year's Ramadan daylight-saving transitions would be +# 2012-07-20 and 2012-08-20; see +# . +# +# To estimate what the Moroccan government will do in future years, +# transition dates for 2013 through 2021 were determined by running +# the following program under GNU Emacs 24.3: +# +# (let ((islamic-year 1434)) +# (while (< islamic-year 1444) +# (let ((a +# (calendar-gregorian-from-absolute +# (calendar-islamic-to-absolute (list 9 1 islamic-year)))) +# (b +# (calendar-gregorian-from-absolute +# (calendar-islamic-to-absolute (list 10 1 islamic-year))))) +# (insert +# (format +# (concat "Rule\tMorocco\t%d\tonly\t-\t%s\t %2d\t 3:00\t0\t-\n" +# "Rule\tMorocco\t%d\tonly\t-\t%s\t %2d\t 2:00\t1:00\tS\n") +# (car (cdr (cdr a))) (calendar-month-name (car a) t) (car (cdr a)) +# (car (cdr (cdr b))) (calendar-month-name (car b) t) (car (cdr b))))) +# (setq islamic-year (+ 1 islamic-year)))) +# +# with the results hand-edited for 2020-2022, when the normal spring-forward +# date falls during the estimated Ramadan. +# +# From 2023 through 2038 Ramadan is not predicted to overlap with +# daylight saving time. Starting in 2039 there will be overlap again, +# but 32-bit time_t values roll around in 2038 so for now do not worry +# about dates after 2038. + # RULE NAME FROM TO TYPE IN ON AT SAVE LETTER/S Rule Morocco 1939 only - Sep 12 0:00 1:00 S @@ -863,10 +929,28 @@ Rule Morocco 2010 only - May 2 0:00 1:00 S Rule Morocco 2010 only - Aug 8 0:00 0 - Rule Morocco 2011 only - Apr 3 0:00 1:00 S Rule Morocco 2011 only - Jul 31 0 0 - -Rule Morocco 2012 max - Apr lastSun 2:00 1:00 S +Rule Morocco 2012 2019 - Apr lastSun 2:00 1:00 S Rule Morocco 2012 max - Sep lastSun 3:00 0 - Rule Morocco 2012 only - Jul 20 3:00 0 - Rule Morocco 2012 only - Aug 20 2:00 1:00 S +Rule Morocco 2013 only - Jul 9 3:00 0 - +Rule Morocco 2013 only - Aug 8 2:00 1:00 S +Rule Morocco 2014 only - Jun 29 3:00 0 - +Rule Morocco 2014 only - Jul 29 2:00 1:00 S +Rule Morocco 2015 only - Jun 18 3:00 0 - +Rule Morocco 2015 only - Jul 18 2:00 1:00 S +Rule Morocco 2016 only - Jun 7 3:00 0 - +Rule Morocco 2016 only - Jul 7 2:00 1:00 S +Rule Morocco 2017 only - May 27 3:00 0 - +Rule Morocco 2017 only - Jun 26 2:00 1:00 S +Rule Morocco 2018 only - May 16 3:00 0 - +Rule Morocco 2018 only - Jun 15 2:00 1:00 S +Rule Morocco 2019 only - May 6 3:00 0 - +Rule Morocco 2019 only - Jun 5 2:00 1:00 S +Rule Morocco 2020 only - May 24 2:00 1:00 S +Rule Morocco 2021 only - May 13 2:00 1:00 S +Rule Morocco 2022 only - May 3 2:00 1:00 S +Rule Morocco 2023 max - Apr lastSun 2:00 1:00 S # Zone NAME GMTOFF RULES FORMAT [UNTIL] Zone Africa/Casablanca -0:30:20 - LMT 1913 Oct 26 diff --git a/jdk/test/sun/util/calendar/zi/tzdata/antarctica b/jdk/test/sun/util/calendar/zi/tzdata/antarctica index 64b71d5c052..daa03ea830c 100644 --- a/jdk/test/sun/util/calendar/zi/tzdata/antarctica +++ b/jdk/test/sun/util/calendar/zi/tzdata/antarctica @@ -73,38 +73,8 @@ Rule ChileAQ 2009 only - Mar Sun>=9 3:00u 0 - Rule ChileAQ 2010 only - Apr Sun>=1 3:00u 0 - Rule ChileAQ 2011 only - May Sun>=2 3:00u 0 - Rule ChileAQ 2011 only - Aug Sun>=16 4:00u 1:00 S -Rule ChileAQ 2012 only - Apr Sun>=23 3:00u 0 - -Rule ChileAQ 2012 only - Sep Sun>=2 4:00u 1:00 S -Rule ChileAQ 2013 max - Mar Sun>=9 3:00u 0 - -Rule ChileAQ 2013 max - Oct Sun>=9 4:00u 1:00 S - -# These rules are stolen from the `australasia' file. -Rule AusAQ 1917 only - Jan 1 0:01 1:00 - -Rule AusAQ 1917 only - Mar 25 2:00 0 - -Rule AusAQ 1942 only - Jan 1 2:00 1:00 - -Rule AusAQ 1942 only - Mar 29 2:00 0 - -Rule AusAQ 1942 only - Sep 27 2:00 1:00 - -Rule AusAQ 1943 1944 - Mar lastSun 2:00 0 - -Rule AusAQ 1943 only - Oct 3 2:00 1:00 - -Rule ATAQ 1967 only - Oct Sun>=1 2:00s 1:00 - -Rule ATAQ 1968 only - Mar lastSun 2:00s 0 - -Rule ATAQ 1968 1985 - Oct lastSun 2:00s 1:00 - -Rule ATAQ 1969 1971 - Mar Sun>=8 2:00s 0 - -Rule ATAQ 1972 only - Feb lastSun 2:00s 0 - -Rule ATAQ 1973 1981 - Mar Sun>=1 2:00s 0 - -Rule ATAQ 1982 1983 - Mar lastSun 2:00s 0 - -Rule ATAQ 1984 1986 - Mar Sun>=1 2:00s 0 - -Rule ATAQ 1986 only - Oct Sun>=15 2:00s 1:00 - -Rule ATAQ 1987 1990 - Mar Sun>=15 2:00s 0 - -Rule ATAQ 1987 only - Oct Sun>=22 2:00s 1:00 - -Rule ATAQ 1988 1990 - Oct lastSun 2:00s 1:00 - -Rule ATAQ 1991 1999 - Oct Sun>=1 2:00s 1:00 - -Rule ATAQ 1991 2005 - Mar lastSun 2:00s 0 - -Rule ATAQ 2000 only - Aug lastSun 2:00s 1:00 - -Rule ATAQ 2001 max - Oct Sun>=1 2:00s 1:00 - -Rule ATAQ 2006 only - Apr Sun>=1 2:00s 0 - -Rule ATAQ 2007 only - Mar lastSun 2:00s 0 - -Rule ATAQ 2008 max - Apr Sun>=1 2:00s 0 - +Rule ChileAQ 2012 max - Apr Sun>=23 3:00u 0 - +Rule ChileAQ 2012 max - Sep Sun>=2 4:00u 1:00 S # Argentina - year-round bases # Belgrano II, Confin Coast, -770227-0343737, since 1972-02-05 @@ -147,10 +117,7 @@ Rule ATAQ 2008 max - Apr Sun>=1 2:00s 0 - # # From Steffen Thorsen (2010-03-10): -# We got these changes from the Australian Antarctic Division: -# - Macquarie Island will stay on UTC+11 for winter and therefore not -# switch back from daylight savings time when other parts of Australia do -# on 4 April. +# We got these changes from the Australian Antarctic Division: ... # # - Casey station reverted to its normal time of UTC+8 on 5 March 2010. # The change to UTC+11 is being considered as a regular summer thing but @@ -161,9 +128,6 @@ Rule ATAQ 2008 max - Apr Sun>=1 2:00s 0 - # # - Mawson station stays on UTC+5. # -# In addition to the Rule changes for Casey/Davis, it means that Macquarie -# will no longer be like Hobart and will have to have its own Zone created. -# # Background: # # http://www.timeanddate.com/news/time/antartica-time-changes-2010.html @@ -190,12 +154,6 @@ Zone Antarctica/Mawson 0 - zzz 1954 Feb 13 6:00 - MAWT 2009 Oct 18 2:00 # Mawson Time 5:00 - MAWT -Zone Antarctica/Macquarie 0 - zzz 1911 - 10:00 - EST 1916 Oct 1 2:00 - 10:00 1:00 EST 1917 Feb - 10:00 AusAQ EST 1967 - 10:00 ATAQ EST 2010 Apr 4 3:00 - 11:00 - MIST # Macquarie Island Time # References: # # Casey Weather (1998-02-26) diff --git a/jdk/test/sun/util/calendar/zi/tzdata/asia b/jdk/test/sun/util/calendar/zi/tzdata/asia index 9ef3ef8df54..7818c029a60 100644 --- a/jdk/test/sun/util/calendar/zi/tzdata/asia +++ b/jdk/test/sun/util/calendar/zi/tzdata/asia @@ -27,9 +27,9 @@ # This data is by no means authoritative; if you think you know better, # go ahead and edit the file (and please send any changes to -# tz@elsie.nci.nih.gov for general use in the future). +# tz@iana.org for general use in the future). -# From Paul Eggert (2006-03-22): +# From Paul Eggert (2013-02-21): # # A good source for time zone historical data outside the U.S. is # Thomas G. Shanks and Rique Pottenger, The International Atlas (6th edition), @@ -48,6 +48,10 @@ # Whitman Publishing Co, 2 Niagara Av, Ealing, London (undated), which # I found in the UCLA library. # +# For data circa 1899, a common source is: +# Milne J. Civil time. Geogr J. 1899 Feb;13(2):173-94 +# . +# # A reliable and entertaining source about time zones is # Derek Howse, Greenwich time and longitude, Philip Wilson Publishers (1997). # @@ -302,9 +306,12 @@ Zone Asia/Brunei 7:39:40 - LMT 1926 Mar # Bandar Seri Begawan 8:00 - BNT # Burma / Myanmar + +# Milne says 6:24:40 was the meridian of the time ball observatory at Rangoon. + # Zone NAME GMTOFF RULES FORMAT [UNTIL] Zone Asia/Rangoon 6:24:40 - LMT 1880 # or Yangon - 6:24:36 - RMT 1920 # Rangoon Mean Time? + 6:24:40 - RMT 1920 # Rangoon Mean Time? 6:30 - BURT 1942 May # Burma Time 9:00 - JST 1945 May 3 6:30 - MMT # Myanmar Time @@ -407,7 +414,8 @@ Zone Asia/Harbin 8:26:44 - LMT 1928 # or Haerbin 8:00 PRC C%sT # Zhongyuan Time ("Central plain Time") # most of China -Zone Asia/Shanghai 8:05:52 - LMT 1928 +# Milne gives 8:05:56.7; round to nearest. +Zone Asia/Shanghai 8:05:57 - LMT 1928 8:00 Shang C%sT 1949 8:00 PRC C%sT # Long-shu Time (probably due to Long and Shu being two names of that area) @@ -504,6 +512,10 @@ Zone Asia/Kashgar 5:03:56 - LMT 1928 # or Kashi or Kaxgar 8:00 PRC C%sT +# Hong Kong (Xianggang) + +# Milne gives 7:36:41.7; round this. + # From Lee Yiu Chung (2009-10-24): # I found there are some mistakes for the...DST rule for Hong # Kong. [According] to the DST record from Hong Kong Observatory (actually, @@ -570,7 +582,6 @@ Zone Asia/Kashgar 5:03:56 - LMT 1928 # or Kashi or Kaxgar # The Japanese surrender of Hong Kong was signed 1945-09-15. # For lack of anything better, use start of those days as the transition times. -# Hong Kong (Xianggang) # Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S Rule HK 1941 only - Apr 1 3:30 1:00 S Rule HK 1941 only - Sep 30 3:30 0 - @@ -592,7 +603,7 @@ Rule HK 1973 only - Dec 30 3:30 1:00 S Rule HK 1979 only - May Sun>=8 3:30 1:00 S Rule HK 1979 only - Oct Sun>=16 3:30 0 - # Zone NAME GMTOFF RULES FORMAT [UNTIL] -Zone Asia/Hong_Kong 7:36:36 - LMT 1904 Oct 30 +Zone Asia/Hong_Kong 7:36:42 - LMT 1904 Oct 30 8:00 HK HK%sT 1941 Dec 25 9:00 - JST 1945 Sep 15 8:00 HK HK%sT @@ -669,6 +680,9 @@ Zone Asia/Macau 7:34:20 - LMT 1912 ############################################################################### # Cyprus +# +# Milne says the Eastern Telegraph Company used 2:14:00. Stick with LMT. +# # Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S Rule Cyprus 1975 only - Apr 13 0:00 1:00 S Rule Cyprus 1975 only - Oct 12 0:00 0 - @@ -1222,7 +1236,6 @@ Rule Zion 2012 only - Mar Fri>=26 2:00 1:00 D Rule Zion 2012 only - Sep 23 2:00 0 S # From Ephraim Silverberg (2012-10-18): - # Yesterday, the Interior Ministry Committee, after more than a year # past, approved sending the proposed June 2011 changes to the Time # Decree Law back to the Knesset for second and third (final) votes @@ -1235,6 +1248,10 @@ Rule Zion 2012 only - Sep 23 2:00 0 S # later (i.e. at 02:00 the first Monday after October 2). # [Rosh Hashana holidays are factored in until 2100.] +# From Ephraim Silverberg (2012-11-05): +# The Knesset passed today (in second and final readings) the amendment to the +# Time Decree Law making the changes ... law. + # Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S Rule Zion 2013 max - Mar Fri>=23 2:00 1:00 D Rule Zion 2013 2026 - Oct Sun>=2 2:00 0 S @@ -1824,8 +1841,11 @@ Zone Asia/Kathmandu 5:41:16 - LMT 1920 5:45 - NPT # Nepal Time # Oman + +# Milne says 3:54:24 was the meridian of the Muscat Tidal Observatory. + # Zone NAME GMTOFF RULES FORMAT [UNTIL] -Zone Asia/Muscat 3:54:20 - LMT 1920 +Zone Asia/Muscat 3:54:24 - LMT 1920 4:00 - GST # Pakistan @@ -2072,8 +2092,7 @@ Zone Asia/Karachi 4:28:12 - LMT 1907 # occurred before our cutoff date of 1970. # However, as we get more information, we may need to add entries # for parts of the West Bank as they transitioned from Israel's rules -# to Palestine's rules. If you have more info about this, please -# send it to tz@elsie.nci.nih.gov for incorporation into future editions. +# to Palestine's rules. # From IINS News Service - Israel - 1998-03-23 10:38:07 Israel time, # forwarded by Ephraim Silverberg: @@ -2295,11 +2314,20 @@ Zone Asia/Karachi 4:28:12 - LMT 1907 # http://www.timeanddate.com/news/time/gaza-west-bank-dst-2012.html # -# From Arthur David Olson (2012-03-27): -# The timeanddate article for 2012 says that "the end date has not yet been -# announced" and that "Last year, both...paused daylight saving time during... -# Ramadan. It is not yet known [for] 2012." -# For now, assume both switch back on the last Friday in September. XXX +# From Steffen Thorsen (2013-03-26): +# The following news sources tells that Palestine will "start daylight saving +# time from midnight on Friday, March 29, 2013" (translated). +# [These are in Arabic and are for Gaza and for Ramallah, respectively.] +# http://www.samanews.com/index.php?act=Show&id=154120 +# http://safa.ps/details/news/99844/%D8%B1%D8%A7%D9%85-%D8%A7%D9%84%D9%84%D9%87-%D8%A8%D8%AF%D8%A1-%D8%A7%D9%84%D8%AA%D9%88%D9%82%D9%8A%D8%AA-%D8%A7%D9%84%D8%B5%D9%8A%D9%81%D9%8A-29-%D8%A7%D9%84%D8%AC%D8%A7%D8%B1%D9%8A.html + +# From Paul Eggert (2013-04-15): +# For future dates, guess the last Thursday in March at 24:00 through +# the first Friday on or after September 21 at 01:00. This is consistent with +# the predictions in today's editions of the following URLs, +# which are for Gaza and Hebron respectively: +# http://www.timeanddate.com/worldclock/timezone.html?n=702 +# http://www.timeanddate.com/worldclock/timezone.html?n=2364 # Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S Rule EgyptAsia 1957 only - May 10 0:00 1:00 S @@ -2313,19 +2341,20 @@ Rule Palestine 1999 2005 - Apr Fri>=15 0:00 1:00 S Rule Palestine 1999 2003 - Oct Fri>=15 0:00 0 - Rule Palestine 2004 only - Oct 1 1:00 0 - Rule Palestine 2005 only - Oct 4 2:00 0 - -Rule Palestine 2006 2008 - Apr 1 0:00 1:00 S +Rule Palestine 2006 2007 - Apr 1 0:00 1:00 S Rule Palestine 2006 only - Sep 22 0:00 0 - Rule Palestine 2007 only - Sep Thu>=8 2:00 0 - -Rule Palestine 2008 only - Aug lastFri 0:00 0 - -Rule Palestine 2009 only - Mar lastFri 0:00 1:00 S -Rule Palestine 2009 only - Sep Fri>=1 2:00 0 - -Rule Palestine 2010 only - Mar lastSat 0:01 1:00 S +Rule Palestine 2008 2009 - Mar lastFri 0:00 1:00 S +Rule Palestine 2008 only - Sep 1 0:00 0 - +Rule Palestine 2009 only - Sep Fri>=1 1:00 0 - +Rule Palestine 2010 only - Mar 26 0:00 1:00 S Rule Palestine 2010 only - Aug 11 0:00 0 - - -# From Arthur David Olson (2011-09-20): -# 2011 transitions per http://www.timeanddate.com as of 2011-09-20. -# From Paul Eggert (2012-10-12): -# 2012 transitions per http://www.timeanddate.com as of 2012-10-12. +Rule Palestine 2011 only - Apr 1 0:01 1:00 S +Rule Palestine 2011 only - Aug 1 0:00 0 - +Rule Palestine 2011 only - Aug 30 0:00 1:00 S +Rule Palestine 2011 only - Sep 30 0:00 0 - +Rule Palestine 2012 max - Mar lastThu 24:00 1:00 S +Rule Palestine 2012 max - Sep Fri>=21 1:00 0 - # Zone NAME GMTOFF RULES FORMAT [UNTIL] Zone Asia/Gaza 2:17:52 - LMT 1900 Oct @@ -2333,26 +2362,20 @@ Zone Asia/Gaza 2:17:52 - LMT 1900 Oct 2:00 EgyptAsia EE%sT 1967 Jun 5 2:00 Zion I%sT 1996 2:00 Jordan EE%sT 1999 - 2:00 Palestine EE%sT 2011 Apr 2 12:01 - 2:00 1:00 EEST 2011 Aug 1 - 2:00 - EET 2012 Mar 30 - 2:00 1:00 EEST 2012 Sep 21 1:00 - 2:00 - EET + 2:00 Palestine EE%sT 2008 Aug 29 0:00 + 2:00 - EET 2008 Sep + 2:00 Palestine EE%sT 2010 + 2:00 - EET 2010 Mar 27 0:01 + 2:00 Palestine EE%sT 2011 Aug 1 + 2:00 - EET 2012 + 2:00 Palestine EE%sT Zone Asia/Hebron 2:20:23 - LMT 1900 Oct 2:00 Zion EET 1948 May 15 2:00 EgyptAsia EE%sT 1967 Jun 5 2:00 Zion I%sT 1996 2:00 Jordan EE%sT 1999 - 2:00 Palestine EE%sT 2008 Aug - 2:00 1:00 EEST 2008 Sep - 2:00 Palestine EE%sT 2011 Apr 1 12:01 - 2:00 1:00 EEST 2011 Aug 1 - 2:00 - EET 2011 Aug 30 - 2:00 1:00 EEST 2011 Sep 30 3:00 - 2:00 - EET 2012 Mar 30 - 2:00 1:00 EEST 2012 Sep 21 1:00 - 2:00 - EET + 2:00 Palestine EE%sT # Paracel Is # no information @@ -2421,6 +2444,13 @@ Zone Asia/Singapore 6:55:25 - LMT 1901 Jan 1 # no information # Sri Lanka + +# From Paul Eggert (2013-02-21): +# Milne says "Madras mean time use from May 1, 1898. Prior to this Colombo +# mean time, 5h. 4m. 21.9s. F., was used." But 5:04:21.9 differs considerably +# from Colombo's meridian 5:19:24, so for now ignore Milne and stick with +# Shanks and Pottenger. + # From Paul Eggert (1996-09-03): # "Sri Lanka advances clock by an hour to avoid blackout" # (www.virtual-pc.com/lankaweb/news/items/240596-2.html, 1996-05-24, @@ -2720,6 +2750,12 @@ Zone Asia/Tashkent 4:37:12 - LMT 1924 May 2 # Vietnam +# From Paul Eggert (2013-02-21): +# Milne gives 7:16:56 for the meridian of Saigon in 1899, as being +# used in Lower Laos, Cambodia, and Annam. But this is quite a ways +# from Saigon's location. For now, ignore this and stick with Shanks +# and Pottenger. + # From Arthur David Olson (2008-03-18): # The English-language name of Vietnam's most populous city is "Ho Chi Min City"; # we use Ho_Chi_Minh below to avoid a name of more than 14 characters. @@ -2733,6 +2769,10 @@ Zone Asia/Ho_Chi_Minh 7:06:40 - LMT 1906 Jun 9 7:00 - ICT # Yemen + +# Milne says 2:59:54 was the meridian of the saluting battery at Aden, +# and that Yemen was at 1:55:56, the meridian of the Hagia Sophia. + # Zone NAME GMTOFF RULES FORMAT [UNTIL] -Zone Asia/Aden 3:00:48 - LMT 1950 +Zone Asia/Aden 2:59:54 - LMT 1950 3:00 - AST diff --git a/jdk/test/sun/util/calendar/zi/tzdata/australasia b/jdk/test/sun/util/calendar/zi/tzdata/australasia index 7f83448f3fb..db954a81dcd 100644 --- a/jdk/test/sun/util/calendar/zi/tzdata/australasia +++ b/jdk/test/sun/util/calendar/zi/tzdata/australasia @@ -241,9 +241,26 @@ Zone Australia/Lord_Howe 10:36:20 - LMT 1895 Feb # no times are set # # Macquarie -# permanent occupation (scientific station) since 1948; -# sealing and penguin oil station operated 1888/1917 -# like Australia/Hobart +# Permanent occupation (scientific station) 1911-1915 and since 25 March 1948; +# sealing and penguin oil station operated Nov 1899 to Apr 1919. See the +# Tasmania Parks & Wildlife Service history of sealing at Macquarie Island +# +# . +# Guess that it was like Australia/Hobart while inhabited before 2010. +# +# From Steffen Thorsen (2010-03-10): +# We got these changes from the Australian Antarctic Division: +# - Macquarie Island will stay on UTC+11 for winter and therefore not +# switch back from daylight savings time when other parts of Australia do +# on 4 April. +Zone Antarctica/Macquarie 0 - zzz 1899 Nov + 10:00 - EST 1916 Oct 1 2:00 + 10:00 1:00 EST 1917 Feb + 10:00 Aus EST 1919 Apr + 0 - zzz 1948 Mar 25 + 10:00 Aus EST 1967 + 10:00 AT EST 2010 Apr 4 3:00 + 11:00 - MIST # Macquarie I Standard Time # Christmas # Zone NAME GMTOFF RULES FORMAT [UNTIL] @@ -269,6 +286,9 @@ Zone Indian/Cocos 6:27:40 - LMT 1900 6:30 - CCT # Cocos Islands Time # Fiji + +# Milne gives 11:55:44 for Suva. + # From Alexander Krivenyshev (2009-11-10): # According to Fiji Broadcasting Corporation, Fiji plans to re-introduce DST # from November 29th 2009 to April 25th 2010. @@ -362,7 +382,7 @@ Rule Fiji 2010 max - Oct Sun>=18 2:00 1:00 S Rule Fiji 2011 only - Mar Sun>=1 3:00 0 - Rule Fiji 2012 max - Jan Sun>=18 3:00 0 - # Zone NAME GMTOFF RULES FORMAT [UNTIL] -Zone Pacific/Fiji 11:53:40 - LMT 1915 Oct 26 # Suva +Zone Pacific/Fiji 11:55:44 - LMT 1915 Oct 26 # Suva 12:00 Fiji FJ%sT # Fiji Time # French Polynesia @@ -803,9 +823,9 @@ Zone Pacific/Wallis 12:15:20 - LMT 1901 # This data is by no means authoritative; if you think you know better, # go ahead and edit the file (and please send any changes to -# tz@elsie.nci.nih.gov for general use in the future). +# tz@iana.org for general use in the future). -# From Paul Eggert (2006-03-22): +# From Paul Eggert (2013-02-21): # A good source for time zone historical data outside the U.S. is # Thomas G. Shanks and Rique Pottenger, The International Atlas (6th edition), # San Diego: ACS Publications, Inc. (2003). @@ -823,6 +843,10 @@ Zone Pacific/Wallis 12:15:20 - LMT 1901 # Whitman Publishing Co, 2 Niagara Av, Ealing, London (undated), which # I found in the UCLA library. # +# For data circa 1899, a common source is: +# Milne J. Civil time. Geogr J. 1899 Feb;13(2):173-94 +# . +# # A reliable and entertaining source about time zones is # Derek Howse, Greenwich time and longitude, Philip Wilson Publishers (1997). # diff --git a/jdk/test/sun/util/calendar/zi/tzdata/europe b/jdk/test/sun/util/calendar/zi/tzdata/europe index 9a0d0b9db94..268504d0983 100644 --- a/jdk/test/sun/util/calendar/zi/tzdata/europe +++ b/jdk/test/sun/util/calendar/zi/tzdata/europe @@ -27,7 +27,7 @@ # This data is by no means authoritative; if you think you know better, # go ahead and edit the file (and please send any changes to -# tz@elsie.nci.nih.gov for general use in the future). +# tz@iana.org for general use in the future). # From Paul Eggert (2006-03-22): # A good source for time zone historical data outside the U.S. is @@ -53,6 +53,12 @@ # William Willett, The Waste of Daylight, 19th edition # (1914-03) # +# Milne J. Civil time. Geogr J. 1899 Feb;13(2):173-94 +# . He writes: +# "It is requested that corrections and additions to these tables +# may be sent to Mr. John Milne, Royal Geographical Society, +# Savile Row, London." Nowadays please email them to tz@iana.org. +# # Brazil's Departamento Servico da Hora (DSH), # # History of Summer Time @@ -689,6 +695,8 @@ Zone Europe/Andorra 0:06:04 - LMT 1901 # Austria +# Milne says Vienna time was 1:05:21. + # From Paul Eggert (2006-03-22): Shanks & Pottenger give 1918-06-16 and # 1945-11-18, but the Austrian Federal Office of Metrology and # Surveying (BEV) gives 1918-09-16 and for Vienna gives the "alleged" @@ -706,7 +714,7 @@ Rule Austria 1948 only - Apr 18 2:00s 1:00 S Rule Austria 1980 only - Apr 6 0:00 1:00 S Rule Austria 1980 only - Sep 28 0:00 0 - # Zone NAME GMTOFF RULES FORMAT [UNTIL] -Zone Europe/Vienna 1:05:20 - LMT 1893 Apr +Zone Europe/Vienna 1:05:21 - LMT 1893 Apr 1:00 C-Eur CE%sT 1920 1:00 Austria CE%sT 1940 Apr 1 2:00s 1:00 C-Eur CE%sT 1945 Apr 2 2:00s @@ -1262,6 +1270,21 @@ Zone Europe/Berlin 0:53:28 - LMT 1893 Apr 1:00 Germany CE%sT 1980 1:00 EU CE%sT +# From Tobias Conradi (2011-09-12): +# Busingen , surrounded by the Swiss canton +# Schaffhausen, did not start observing DST in 1980 as the rest of DE +# (West Germany at that time) and DD (East Germany at that time) did. +# DD merged into DE, the area is currently covered by code DE in ISO 3166-1, +# which in turn is covered by the zone Europe/Berlin. +# +# Source for the time in Busingen 1980: +# http://www.srf.ch/player/video?id=c012c029-03b7-4c2b-9164-aa5902cd58d3 + +# From Arthur David Olson (2012-03-03): +# Busingen and Zurich have shared clocks since 1970. + +Link Europe/Zurich Europe/Busingen + # Georgia # Please see the "asia" file for Asia/Tbilisi. # Herodotus (Histories, IV.45) says Georgia north of the Phasis (now Rioni) @@ -2066,6 +2089,70 @@ Zone Europe/Bucharest 1:44:24 - LMT 1891 Oct # Russia +# From Alexander Krivenyshev (2011-09-15): +# Based on last Russian Government Decree # 725 on August 31, 2011 +# (Government document +# +# http://www.government.ru/gov/results/16355/print/ +# +# in Russian) +# there are few corrections have to be made for some Russian time zones... +# All updated Russian Time Zones were placed in table and translated to English +# by WorldTimeZone.com at the link below: +# +# http://www.worldtimezone.com/dst_news/dst_news_russia36.htm +# + +# From Sanjeev Gupta (2011-09-27): +# Scans of [Decree #23 of January 8, 1992] are available at: +# +# http://government.consultant.ru/page.aspx?1223966 +# They are in Cyrillic letters (presumably Russian). + +# From Arthur David Olson (2012-05-09): +# Regarding the instant when clocks in time-zone-shifting parts of Russia +# changed in September 2011: +# +# One source is +# < a href="http://government.ru/gov/results/16355/> +# http://government.ru/gov/results/16355/ +# +# which, according to translate.google.com, begins "Decree of August 31, +# 2011 No 725" and contains no other dates or "effective date" information. +# +# Another source is +# +# http://www.rg.ru/2011/09/06/chas-zona-dok.html +# +# which, according to translate.google.com, begins "Resolution of the +# Government of the Russian Federation on August 31, 2011 N 725" and also +# contains "Date first official publication: September 6, 2011 Posted on: +# in the 'RG' - Federal Issue number 5573 September 6, 2011" but which +# does not contain any "effective date" information. +# +# Another source is +# +# http://en.wikipedia.org/wiki/Oymyakonsky_District#cite_note-RuTime-7 +# +# which, in note 8, contains "Resolution #725 of August 31, 2011... +# Effective as of after 7 days following the day of the official publication" +# but which does not contain any reference to September 6, 2011. +# +# The Wikipedia article refers to +# +# http://base.consultant.ru/cons/cgi/online.cgi?req=doc;base=LAW;n=118896 +# +# which seems to copy the text of the government.ru page. +# +# Tobias Conradi combines Wikipedia's +# "as of after 7 days following the day of the official publication" +# with www.rg.ru's "Date of first official publication: September 6, 2011" to get +# September 13, 2011 as the cutover date (unusually, a Tuesday, as Tobias Conradi notes). +# +# None of the sources indicates a time of day for changing clocks. +# +# Go with 2011-09-13 0:00s. + # From Paul Eggert (2006-03-22): # Except for Moscow after 1919-07-01, I invented the time zone abbreviations. # Moscow time zone abbreviations after 1919-07-01, and Moscow rules after 1991, @@ -2293,14 +2380,32 @@ Zone Asia/Yakutsk 8:38:40 - LMT 1919 Dec 15 # [parts of] Respublika Sakha (Yakutiya). # From Oscar van Vlijmen (2009-11-29): -# The Sakha districts are: Bulunskij, Verkhoyanskij, Tomponskij, Ust'-Majskij, -# Ust'-Yanskij. +# The Sakha districts are: Bulunskij, Verkhoyanskij, ... Ust'-Yanskij. Zone Asia/Vladivostok 8:47:44 - LMT 1922 Nov 15 9:00 - VLAT 1930 Jun 21 # Vladivostok Time 10:00 Russia VLA%sT 1991 Mar 31 2:00s 9:00 Russia VLA%sST 1992 Jan 19 2:00s 10:00 Russia VLA%sT 2011 Mar 27 2:00s 11:00 - VLAT + +# From Arthur David Olson (2012-05-09): +# Tomponskij and Ust'-Majskij switched from Vladivostok time to Yakutsk time +# in 2011. +# +# From Paul Eggert (2012-11-25): +# Shanks and Pottenger (2003) has Khandyga on Yakutsk time. +# Make a wild guess that it switched to Vladivostok time in 2004. +# This transition is no doubt wrong, but we have no better info. +# +Zone Asia/Khandyga 9:02:13 - LMT 1919 Dec 15 + 8:00 - YAKT 1930 Jun 21 # Yakutsk Time + 9:00 Russia YAK%sT 1991 Mar 31 2:00s + 8:00 Russia YAK%sT 1992 Jan 19 2:00s + 9:00 Russia YAK%sT 2004 + 10:00 Russia VLA%sT 2011 Mar 27 2:00s + 11:00 - VLAT 2011 Sep 13 0:00s # Decree 725? + 10:00 - YAKT + # # Sakhalinskaya oblast'. # The Zone name should be Yuzhno-Sakhalinsk, but that's too long. @@ -2319,14 +2424,26 @@ Zone Asia/Sakhalin 9:30:48 - LMT 1905 Aug 23 # From Oscar van Vlijmen (2009-11-29): # The Sakha districts are: Abyjskij, Allaikhovskij, Verkhhhnekolymskij, Momskij, -# Nizhnekolymskij, Ojmyakonskij, Srednekolymskij. +# Nizhnekolymskij, ... Srednekolymskij. Zone Asia/Magadan 10:03:12 - LMT 1924 May 2 10:00 - MAGT 1930 Jun 21 # Magadan Time 11:00 Russia MAG%sT 1991 Mar 31 2:00s 10:00 Russia MAG%sT 1992 Jan 19 2:00s 11:00 Russia MAG%sT 2011 Mar 27 2:00s 12:00 - MAGT -# + +# From Arthur David Olson (2012-05-09): +# Ojmyakonskij and the Kuril Islands switched from +# Magadan time to Vladivostok time in 2011. +Zone Asia/Ust-Nera 9:32:54 - LMT 1919 Dec 15 + 8:00 - YAKT 1930 Jun 21 # Yakutsk Time + 9:00 Russia YAKT 1981 Apr 1 + 11:00 Russia MAG%sT 1991 Mar 31 2:00s + 10:00 Russia MAG%sT 1992 Jan 19 2:00s + 11:00 Russia MAG%sT 2011 Mar 27 2:00s + 12:00 - MAGT 2011 Sep 13 0:00s # Decree 725? + 11:00 - VLAT + # From Oscar van Vlijmen (2001-08-25): [This region consists of] # Kamchatskaya oblast', Koryakskij avtonomnyj okrug. # diff --git a/jdk/test/sun/util/calendar/zi/tzdata/northamerica b/jdk/test/sun/util/calendar/zi/tzdata/northamerica index c3033267404..858bf811ac9 100644 --- a/jdk/test/sun/util/calendar/zi/tzdata/northamerica +++ b/jdk/test/sun/util/calendar/zi/tzdata/northamerica @@ -29,7 +29,7 @@ # This data is by no means authoritative; if you think you know better, # go ahead and edit the file (and please send any changes to -# tz@elsie.nci.nih.gov for general use in the future). +# tz@iana.org for general use in the future). # From Paul Eggert (1999-03-22): # A reliable and entertaining source about time zones is @@ -1042,6 +1042,9 @@ Zone America/Menominee -5:50:27 - LMT 1885 Sep 18 12:00 # William Willett, The Waste of Daylight, 19th edition # (1914-03) # +# Milne J. Civil time. Geogr J. 1899 Feb;13(2):173-94 +# . +# # See the `europe' file for Greenland. # Canada @@ -2577,6 +2580,8 @@ Zone America/Antigua -4:07:12 - LMT 1912 Mar 2 # Bahamas # +# For 1899 Milne gives -5:09:29.5; round that. +# # From Sue Williams (2006-12-07): # The Bahamas announced about a month ago that they plan to change their DST # rules to sync with the U.S. starting in 2007.... @@ -2586,11 +2591,14 @@ Zone America/Antigua -4:07:12 - LMT 1912 Mar 2 Rule Bahamas 1964 1975 - Oct lastSun 2:00 0 S Rule Bahamas 1964 1975 - Apr lastSun 2:00 1:00 D # Zone NAME GMTOFF RULES FORMAT [UNTIL] -Zone America/Nassau -5:09:24 - LMT 1912 Mar 2 +Zone America/Nassau -5:09:30 - LMT 1912 Mar 2 -5:00 Bahamas E%sT 1976 -5:00 US E%sT # Barbados + +# For 1899 Milne gives -3:58:29.2; round that. + # Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S Rule Barb 1977 only - Jun 12 2:00 1:00 D Rule Barb 1977 1978 - Oct Sun>=1 2:00 0 S @@ -2598,8 +2606,8 @@ Rule Barb 1978 1980 - Apr Sun>=15 2:00 1:00 D Rule Barb 1979 only - Sep 30 2:00 0 S Rule Barb 1980 only - Sep 25 2:00 0 S # Zone NAME GMTOFF RULES FORMAT [UNTIL] -Zone America/Barbados -3:58:28 - LMT 1924 # Bridgetown - -3:58:28 - BMT 1932 # Bridgetown Mean Time +Zone America/Barbados -3:58:29 - LMT 1924 # Bridgetown + -3:58:29 - BMT 1932 # Bridgetown Mean Time -4:00 Barb A%sT # Belize @@ -2617,6 +2625,9 @@ Zone America/Belize -5:52:48 - LMT 1912 Apr # Bermuda +# For 1899 Milne gives -4:19:18.3 as the meridian of the clock tower, +# Bermuda dockyard, Ireland I; round that. + # From Dan Jones, reporting in The Royal Gazette (2006-06-26): # Next year, however, clocks in the US will go forward on the second Sunday @@ -2626,7 +2637,7 @@ Zone America/Belize -5:52:48 - LMT 1912 Apr # http://www.theroyalgazette.com/apps/pbcs.dll/article?AID=/20060529/NEWS/105290135 # Zone NAME GMTOFF RULES FORMAT [UNTIL] -Zone Atlantic/Bermuda -4:19:04 - LMT 1930 Jan 1 2:00 # Hamilton +Zone Atlantic/Bermuda -4:19:18 - LMT 1930 Jan 1 2:00 # Hamilton -4:00 - AST 1974 Apr 28 2:00 -4:00 Bahamas A%sT 1976 -4:00 US A%sT @@ -2638,6 +2649,9 @@ Zone America/Cayman -5:25:32 - LMT 1890 # Georgetown -5:00 - EST # Costa Rica + +# Milne gives -5:36:13.3 as San Jose mean time; round to nearest. + # Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S Rule CR 1979 1980 - Feb lastSun 0:00 1:00 D Rule CR 1979 1980 - Jun Sun>=1 0:00 0 S @@ -2648,14 +2662,19 @@ Rule CR 1991 only - Jul 1 0:00 0 S Rule CR 1992 only - Mar 15 0:00 0 S # There are too many San Joses elsewhere, so we'll use `Costa Rica'. # Zone NAME GMTOFF RULES FORMAT [UNTIL] -Zone America/Costa_Rica -5:36:20 - LMT 1890 # San Jose - -5:36:20 - SJMT 1921 Jan 15 # San Jose Mean Time +Zone America/Costa_Rica -5:36:13 - LMT 1890 # San Jose + -5:36:13 - SJMT 1921 Jan 15 # San Jose Mean Time -6:00 CR C%sT # Coco # no information; probably like America/Costa_Rica # Cuba +# From Paul Eggert (2013-02-21): +# Milne gives -5:28:50.45 for the observatory at Havana, -5:29:23.57 +# for the port, and -5:30 for meteorological observations. +# For now, stick with Shanks & Pottenger. + # From Arthur David Olson (1999-03-29): # The 1999-03-28 exhibition baseball game held in Havana, Cuba, between # the Cuban National Team and the Baltimore Orioles was carried live on @@ -3004,24 +3023,21 @@ Zone America/Guatemala -6:02:04 - LMT 1918 Oct 5 # apparently using the same start and end date as USA/Canada. # So this means they have already changed their time. # -# (Sources in French): -# # http://www.alterpresse.org/spip.php?article12510 -# -# # http://radiovision2000haiti.net/home/?p=13253 -# # -# Our coverage: -# -# http://www.timeanddate.com/news/time/haiti-dst-2012.html -# - # From Arthur David Olson (2012-03-11): # The alterpresse.org source seems to show a US-style leap from 2:00 a.m. to # 3:00 a.m. rather than the traditional Haitian jump at midnight. -# Assume a US-style fall back as well XXX. -# Do not yet assume that the change carries forward past 2012 XXX. +# Assume a US-style fall back as well. + +# From Steffen Thorsen (2013-03-10): +# It appears that Haiti is observing DST this year as well, same rules +# as US/Canada. They did it last year as well, and it looks like they +# are going to observe DST every year now... +# +# http://radiovision2000haiti.net/public/haiti-avis-changement-dheure-dimanche/ +# http://www.canalplushaiti.net/?p=6714 # Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S Rule Haiti 1983 only - May 8 0:00 1:00 D @@ -3033,8 +3049,8 @@ Rule Haiti 1988 1997 - Apr Sun>=1 1:00s 1:00 D Rule Haiti 1988 1997 - Oct lastSun 1:00s 0 S Rule Haiti 2005 2006 - Apr Sun>=1 0:00 1:00 D Rule Haiti 2005 2006 - Oct lastSun 0:00 0 S -Rule Haiti 2012 only - Mar Sun>=8 2:00 1:00 D -Rule Haiti 2012 only - Nov Sun>=1 2:00 0 S +Rule Haiti 2012 max - Mar Sun>=8 2:00 1:00 D +Rule Haiti 2012 max - Nov Sun>=1 2:00 0 S # Zone NAME GMTOFF RULES FORMAT [UNTIL] Zone America/Port-au-Prince -4:49:20 - LMT 1890 -4:49 - PPMT 1917 Jan 24 12:00 # P-a-P MT diff --git a/jdk/test/sun/util/calendar/zi/tzdata/southamerica b/jdk/test/sun/util/calendar/zi/tzdata/southamerica index 0d6797eab6b..d1865d3f19b 100644 --- a/jdk/test/sun/util/calendar/zi/tzdata/southamerica +++ b/jdk/test/sun/util/calendar/zi/tzdata/southamerica @@ -27,13 +27,17 @@ # This data is by no means authoritative; if you think you know better, # go ahead and edit the file (and please send any changes to -# tz@elsie.nci.nih.gov for general use in the future). +# tz@iana.org for general use in the future). # From Paul Eggert (2006-03-22): # A good source for time zone historical data outside the U.S. is # Thomas G. Shanks and Rique Pottenger, The International Atlas (6th edition), # San Diego: ACS Publications, Inc. (2003). # +# For data circa 1899, a common source is: +# Milne J. Civil time. Geogr J. 1899 Feb;13(2):173-94 +# . +# # Gwillim Law writes that a good source # for recent time zone data is the International Air Transport # Association's Standard Schedules Information Manual (IATA SSIM), @@ -404,21 +408,11 @@ Rule Arg 2008 only - Oct Sun>=15 0:00 1:00 S # # is the official page for the Province Government). # -# There's also a note in only one of the major national papers (La Nación) at -# +# There's also a note in only one of the major national papers ... # http://www.lanacion.com.ar/nota.asp?nota_id=1107912 -# # -# The press release says: -# (...) anunció que el próximo domingo a las 00:00 los puntanos deberán -# atrasar una hora sus relojes. -# -# A partir de entonces, San Luis establecerá el huso horario propio de -# la Provincia. De esta manera, durante el periodo del calendario anual -# 2009, el cambio horario quedará comprendido entre las 00:00 del tercer -# domingo de marzo y las 24:00 del segundo sábado de octubre. -# Quick&dirty translation -# (...) announced that next Sunday, at 00:00, Puntanos (the San Luis +# The press release says [quick and dirty translation]: +# ... announced that next Sunday, at 00:00, Puntanos (the San Luis # inhabitants) will have to turn back one hour their clocks # # Since then, San Luis will establish its own Province timezone. Thus, @@ -480,6 +474,9 @@ Rule Arg 2008 only - Oct Sun>=15 0:00 1:00 S # rules...San Luis is still using "Western ARgentina Time" and it got # stuck on Summer daylight savings time even though the summer is over. +# From Paul Eggert (2013-02-21): +# Milne says Cordoba time was -4:16:48.2. Round to the nearest second. + # Zone NAME GMTOFF RULES FORMAT [UNTIL] # # Buenos Aires (BA), Capital Federal (CF), @@ -835,9 +832,9 @@ Zone America/La_Paz -4:32:36 - LMT 1890 # From Guilherme Bernardes Rodrigues (2011-10-07): # There is news in the media, however there is still no decree about it. -# I just send a e-mail to Zulmira Brandão at +# I just send a e-mail to Zulmira Brandao at # http://pcdsh01.on.br/ the -# oficial agency about time in Brazil, and she confirmed that the old rule is +# official agency about time in Brazil, and she confirmed that the old rule is # still in force. # From Guilherme Bernardes Rodrigues (2011-10-14) @@ -1266,9 +1263,13 @@ Zone America/Rio_Branco -4:31:12 - LMT 1914 # b. Saturday, September 1, 2012, clocks should go forward 60 minutes; that is, # at 23:59:59, instead of passing to 0:00, the time should be adjusted to be # 01:00 on September 2. -# -# Note that...this is yet another "temporary" change that will be reevaluated -# AGAIN in 2013. + +# From Steffen Thorsen (2013-02-15): +# According to several news sources, Chile has extended DST this year, +# they will end DST later and start DST earlier than planned. They +# hope to save energy. The new end date is 2013-04-28 00:00 and new +# start date is 2013-09-08 00:00.... +# http://www.gob.cl/informa/2013/02/15/gobierno-anuncia-fechas-de-cambio-de-hora-para-el-ano-2013.htm # NOTE: ChileAQ rules for Antarctic bases are stored separately in the # 'antarctica' file. @@ -1311,10 +1312,8 @@ Rule Chile 2009 only - Mar Sun>=9 3:00u 0 - Rule Chile 2010 only - Apr Sun>=1 3:00u 0 - Rule Chile 2011 only - May Sun>=2 3:00u 0 - Rule Chile 2011 only - Aug Sun>=16 4:00u 1:00 S -Rule Chile 2012 only - Apr Sun>=23 3:00u 0 - -Rule Chile 2012 only - Sep Sun>=2 4:00u 1:00 S -Rule Chile 2013 max - Mar Sun>=9 3:00u 0 - -Rule Chile 2013 max - Oct Sun>=9 4:00u 1:00 S +Rule Chile 2012 max - Apr Sun>=23 3:00u 0 - +Rule Chile 2012 max - Sep Sun>=2 4:00u 1:00 S # IATA SSIM anomalies: (1992-02) says 1992-03-14; # (1996-09) says 1998-03-08. Ignore these. # Zone NAME GMTOFF RULES FORMAT [UNTIL] @@ -1336,17 +1335,23 @@ Zone Pacific/Easter -7:17:44 - LMT 1890 # San Felix, and Antarctic bases, are like America/Santiago. # Colombia + +# Milne gives 4:56:16.4 for Bogota time in 1899; round to nearest. He writes, +# "A variation of fifteen minutes in the public clocks of Bogota is not rare." + # Rule NAME FROM TO TYPE IN ON AT SAVE LETTER/S Rule CO 1992 only - May 3 0:00 1:00 S Rule CO 1993 only - Apr 4 0:00 0 - # Zone NAME GMTOFF RULES FORMAT [UNTIL] -Zone America/Bogota -4:56:20 - LMT 1884 Mar 13 - -4:56:20 - BMT 1914 Nov 23 # Bogota Mean Time +Zone America/Bogota -4:56:16 - LMT 1884 Mar 13 + -4:56:16 - BMT 1914 Nov 23 # Bogota Mean Time -5:00 CO CO%sT # Colombia Time # Malpelo, Providencia, San Andres # no information; probably like America/Bogota # Curacao + +# Milne gives 4:35:46.9 for Curacao mean time; round to nearest. # # From Paul Eggert (2006-03-22): # Shanks & Pottenger say that The Bottom and Philipsburg have been at @@ -1363,7 +1368,7 @@ Zone America/Bogota -4:56:20 - LMT 1884 Mar 13 # though, as far as we know. # # Zone NAME GMTOFF RULES FORMAT [UNTIL] -Zone America/Curacao -4:35:44 - LMT 1912 Feb 12 # Willemstad +Zone America/Curacao -4:35:47 - LMT 1912 Feb 12 # Willemstad -4:30 - ANT 1965 # Netherlands Antilles Time -4:00 - AST @@ -1377,6 +1382,8 @@ Link America/Curacao America/Kralendijk # Bonaire, Sint Estatius and Saba # Ecuador # +# Milne says the Sentral and South American Telegraph Company used -5:24:15. +# # From Paul Eggert (2007-03-04): # Apparently Ecuador had a failed experiment with DST in 1992. # (2007-02-27) and @@ -1582,7 +1589,16 @@ Rule Para 2005 2009 - Mar Sun>=8 0:00 0 - # forward 60 minutes, in all the territory of the Paraguayan Republic. # ... Rule Para 2010 max - Oct Sun>=1 0:00 1:00 S -Rule Para 2010 max - Apr Sun>=8 0:00 0 - +Rule Para 2010 2012 - Apr Sun>=8 0:00 0 - +# +# From Steffen Thorsen (2013-03-07): +# Paraguay will end DST on 2013-03-24 00:00.... +# http://www.ande.gov.py/interna.php?id=1075 +# +# From Carlos Raul Perasso (2013-03-15): +# The change in Paraguay is now final. Decree number 10780 +# http://www.presidencia.gov.py/uploads/pdf/presidencia-3b86ff4b691c79d4f5927ca964922ec74772ce857c02ca054a52a37b49afc7fb.pdf +Rule Para 2013 max - Mar Sun>=22 0:00 0 - # Zone NAME GMTOFF RULES FORMAT [UNTIL] Zone America/Asuncion -3:50:40 - LMT 1890 diff --git a/jdk/test/sun/util/calendar/zi/tzdata/zone.tab b/jdk/test/sun/util/calendar/zi/tzdata/zone.tab index ef380cd19fb..cbcdc075bf7 100644 --- a/jdk/test/sun/util/calendar/zi/tzdata/zone.tab +++ b/jdk/test/sun/util/calendar/zi/tzdata/zone.tab @@ -65,7 +65,6 @@ AQ -6617+11031 Antarctica/Casey Casey Station, Bailey Peninsula AQ -7824+10654 Antarctica/Vostok Vostok Station, Lake Vostok AQ -6640+14001 Antarctica/DumontDUrville Dumont-d'Urville Station, Terre Adelie AQ -690022+0393524 Antarctica/Syowa Syowa Station, E Ongul I -AQ -5430+15857 Antarctica/Macquarie Macquarie Island Station, Macquarie Island AR -3436-05827 America/Argentina/Buenos_Aires Buenos Aires (BA, CF) AR -3124-06411 America/Argentina/Cordoba most locations (CB, CC, CN, ER, FM, MN, SE, SF) AR -2447-06525 America/Argentina/Salta (SA, LP, NQ, RN) @@ -81,6 +80,7 @@ AR -5448-06818 America/Argentina/Ushuaia Tierra del Fuego (TF) AS -1416-17042 Pacific/Pago_Pago AT +4813+01620 Europe/Vienna AU -3133+15905 Australia/Lord_Howe Lord Howe Island +AU -5430+15857 Antarctica/Macquarie Macquarie Island AU -4253+14719 Australia/Hobart Tasmania - most locations AU -3956+14352 Australia/Currie Tasmania - King Island AU -3749+14458 Australia/Melbourne Victoria @@ -182,7 +182,8 @@ CW +1211-06900 America/Curacao CX -1025+10543 Indian/Christmas CY +3510+03322 Asia/Nicosia CZ +5005+01426 Europe/Prague -DE +5230+01322 Europe/Berlin +DE +5230+01322 Europe/Berlin most locations +DE +4742+00841 Europe/Busingen Busingen DJ +1136+04309 Africa/Djibouti DK +5540+01235 Europe/Copenhagen DM +1518-06124 America/Dominica @@ -364,8 +365,10 @@ RU +5345+08707 Asia/Novokuznetsk Moscow+03 - Novokuznetsk RU +5601+09250 Asia/Krasnoyarsk Moscow+04 - Yenisei River RU +5216+10420 Asia/Irkutsk Moscow+05 - Lake Baikal RU +6200+12940 Asia/Yakutsk Moscow+06 - Lena River +RU +623923+1353314 Asia/Khandyga Moscow+06 - Tomponsky, Ust-Maysky RU +4310+13156 Asia/Vladivostok Moscow+07 - Amur River RU +4658+14242 Asia/Sakhalin Moscow+07 - Sakhalin Island +RU +643337+1431336 Asia/Ust-Nera Moscow+07 - Oymyakonsky RU +5934+15048 Asia/Magadan Moscow+08 - Magadan RU +5301+15839 Asia/Kamchatka Moscow+08 - Kamchatka RU +6445+17729 Asia/Anadyr Moscow+08 - Bering Sea