001 /* 002 * Licensed to the Apache Software Foundation (ASF) under one or more 003 * contributor license agreements. See the NOTICE file distributed with 004 * this work for additional information regarding copyright ownership. 005 * The ASF licenses this file to You under the Apache License, Version 2.0 006 * (the "License"); you may not use this file except in compliance with 007 * the License. You may obtain a copy of the License at 008 * 009 * http://www.apache.org/licenses/LICENSE-2.0 010 * 011 * Unless required by applicable law or agreed to in writing, software 012 * distributed under the License is distributed on an "AS IS" BASIS, 013 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 014 * See the License for the specific language governing permissions and 015 * limitations under the License. 016 */ 017 package org.apache.commons.net.ftp.parser; 018 019 import java.text.Format; 020 import java.text.ParseException; 021 import java.text.SimpleDateFormat; 022 import java.util.Calendar; 023 import java.util.Date; 024 import java.util.GregorianCalendar; 025 import java.util.TimeZone; 026 027 import org.apache.commons.net.ftp.FTPClientConfig; 028 029 import junit.framework.TestCase; 030 import junit.framework.TestSuite; 031 032 /** 033 * Test the FTPTimestampParser class. 034 * 035 * @author scohen 036 * 037 */ 038 public class FTPTimestampParserImplTest extends TestCase { 039 040 private static final int TWO_HOURS_OF_MILLISECONDS = 2 * 60 * 60 * 1000; 041 042 public void testParseTimestamp() { 043 Calendar cal = Calendar.getInstance(); 044 cal.add(Calendar.HOUR_OF_DAY, 1); 045 cal.set(Calendar.SECOND,0); 046 cal.set(Calendar.MILLISECOND,0); 047 Date anHourFromNow = cal.getTime(); 048 FTPTimestampParserImpl parser = new FTPTimestampParserImpl(); 049 SimpleDateFormat sdf = 050 new SimpleDateFormat(parser.getRecentDateFormatString()); 051 String fmtTime = sdf.format(anHourFromNow); 052 try { 053 Calendar parsed = parser.parseTimestamp(fmtTime); 054 // since the timestamp is ahead of now (by one hour), 055 // this must mean the file's date refers to a year ago. 056 assertEquals("test.roll.back.year", 1, cal.get(Calendar.YEAR) - parsed.get(Calendar.YEAR)); 057 } catch (ParseException e) { 058 fail("Unable to parse"); 059 } 060 } 061 062 public void testParseTimestampWithSlop() { 063 Calendar cal = Calendar.getInstance(); 064 cal.add(Calendar.HOUR_OF_DAY, 1); 065 cal.set(Calendar.SECOND,0); 066 cal.set(Calendar.MILLISECOND,0); 067 Date anHourFromNow = cal.getTime(); 068 cal.add(Calendar.DATE, 1); 069 Date anHourFromNowTomorrow = cal.getTime(); 070 cal.add(Calendar.DATE, -1); 071 072 FTPTimestampParserImpl parser = new FTPTimestampParserImpl(); 073 074 // set the "slop" factor on 075 parser.setLenientFutureDates(true); 076 077 SimpleDateFormat sdf = 078 new SimpleDateFormat(parser.getRecentDateFormatString()); 079 try { 080 String fmtTime = sdf.format(anHourFromNow); 081 Calendar parsed = parser.parseTimestamp(fmtTime); 082 // the timestamp is ahead of now (by one hour), but 083 // that's within range of the "slop" factor. 084 // so the date is still considered this year. 085 assertEquals("test.slop.no.roll.back.year", 0, cal.get(Calendar.YEAR) - parsed.get(Calendar.YEAR)); 086 087 // add a day to get beyond the range of the slop factor. 088 // this must mean the file's date refers to a year ago. 089 fmtTime = sdf.format(anHourFromNowTomorrow); 090 parsed = parser.parseTimestamp(fmtTime); 091 assertEquals("test.slop.roll.back.year", 1, cal.get(Calendar.YEAR) - parsed.get(Calendar.YEAR)); 092 093 } catch (ParseException e) { 094 fail("Unable to parse"); 095 } 096 } 097 098 public void testParseTimestampAcrossTimeZones() { 099 100 101 Calendar cal = Calendar.getInstance(); 102 cal.set(Calendar.SECOND,0); 103 cal.set(Calendar.MILLISECOND,0); 104 105 cal.add(Calendar.HOUR_OF_DAY, 1); 106 Date anHourFromNow = cal.getTime(); 107 108 cal.add(Calendar.HOUR_OF_DAY, 2); 109 Date threeHoursFromNow = cal.getTime(); 110 cal.add(Calendar.HOUR_OF_DAY, -2); 111 112 FTPTimestampParserImpl parser = new FTPTimestampParserImpl(); 113 114 // assume we are FTPing a server in Chicago, two hours ahead of 115 // L. A. 116 FTPClientConfig config = 117 new FTPClientConfig(FTPClientConfig.SYST_UNIX); 118 config.setDefaultDateFormatStr(FTPTimestampParser.DEFAULT_SDF); 119 config.setRecentDateFormatStr(FTPTimestampParser.DEFAULT_RECENT_SDF); 120 // 2 hours difference 121 config.setServerTimeZoneId("America/Chicago"); 122 parser.configure(config); 123 124 SimpleDateFormat sdf = (SimpleDateFormat) 125 parser.getRecentDateFormat().clone(); 126 127 // assume we're in the US Pacific Time Zone 128 TimeZone tzla = TimeZone.getTimeZone("America/Los_Angeles"); 129 sdf.setTimeZone(tzla); 130 131 // get formatted versions of time in L.A. 132 String fmtTimePlusOneHour = sdf.format(anHourFromNow); 133 String fmtTimePlusThreeHours = sdf.format(threeHoursFromNow); 134 135 136 try { 137 Calendar parsed = parser.parseTimestamp(fmtTimePlusOneHour); 138 // the only difference should be the two hours 139 // difference, no rolling back a year should occur. 140 assertEquals("no.rollback.because.of.time.zones", 141 TWO_HOURS_OF_MILLISECONDS, 142 cal.getTime().getTime() - parsed.getTime().getTime()); 143 } catch (ParseException e){ 144 fail("Unable to parse " + fmtTimePlusOneHour); 145 } 146 147 //but if the file's timestamp is THREE hours ahead of now, that should 148 //cause a rollover even taking the time zone difference into account. 149 //Since that time is still later than ours, it is parsed as occurring 150 //on this date last year. 151 try { 152 Calendar parsed = parser.parseTimestamp(fmtTimePlusThreeHours); 153 // rollback should occur here. 154 assertEquals("rollback.even.with.time.zones", 155 1, cal.get(Calendar.YEAR) - parsed.get(Calendar.YEAR)); 156 } catch (ParseException e){ 157 fail("Unable to parse" + fmtTimePlusThreeHours); 158 } 159 } 160 161 162 public void testParser() { 163 FTPTimestampParserImpl parser = new FTPTimestampParserImpl(); 164 try { 165 parser.parseTimestamp("feb 22 2002"); 166 } catch (ParseException e) { 167 fail("failed.to.parse.default"); 168 } 169 try { 170 parser.parseTimestamp("f\u00e9v 22 2002"); 171 fail("should.have.failed.to.parse.default"); 172 } catch (ParseException e) { 173 // this is the success case 174 } 175 176 FTPClientConfig config = new FTPClientConfig(); 177 config.setDefaultDateFormatStr("d MMM yyyy"); 178 config.setRecentDateFormatStr("d MMM HH:mm"); 179 config.setServerLanguageCode("fr"); 180 parser.configure(config); 181 try { 182 parser.parseTimestamp("d\u00e9c 22 2002"); 183 fail("incorrect.field.order"); 184 } catch (ParseException e) { 185 // this is the success case 186 } 187 try { 188 parser.parseTimestamp("22 d\u00e9c 2002"); 189 } catch (ParseException e) { 190 fail("failed.to.parse.french"); 191 } 192 193 try { 194 parser.parseTimestamp("22 dec 2002"); 195 fail("incorrect.language"); 196 } catch (ParseException e) { 197 // this is the success case 198 } 199 try { 200 parser.parseTimestamp("29 f\u00e9v 2002"); 201 fail("nonexistent.date"); 202 } catch (ParseException e) { 203 // this is the success case 204 } 205 206 try { 207 parser.parseTimestamp("22 ao\u00fb 30:02"); 208 fail("bad.hour"); 209 } catch (ParseException e) { 210 // this is the success case 211 } 212 213 try { 214 parser.parseTimestamp("22 ao\u00fb 20:74"); 215 fail("bad.minute"); 216 } catch (ParseException e) { 217 // this is the success case 218 } 219 try { 220 parser.parseTimestamp("28 ao\u00fb 20:02"); 221 } catch (ParseException e) { 222 fail("failed.to.parse.french.recent"); 223 } 224 } 225 226 /* 227 * Check how short date is interpreted at a given time. 228 * Check both with and without lenient future dates 229 */ 230 private void checkShortParse(String msg, Calendar now, Calendar input) throws ParseException { 231 checkShortParse(msg, now, input, false); 232 checkShortParse(msg, now, input, true); 233 } 234 235 /* 236 * Check how short date is interpreted at a given time 237 * Check only using specified lenient future dates setting 238 */ 239 private void checkShortParse(String msg, Calendar now, Calendar input, boolean lenient) throws ParseException { 240 FTPTimestampParserImpl parser = new FTPTimestampParserImpl(); 241 parser.setLenientFutureDates(lenient); 242 Format shortFormat = parser.getRecentDateFormat(); // It's expecting this format 243 Format longFormat = new SimpleDateFormat("yyyy-MM-dd hh:mm"); 244 245 final String shortDate = shortFormat.format(input.getTime()); 246 Calendar output=parser.parseTimestamp(shortDate, now); 247 int outyear = output.get(Calendar.YEAR); 248 int outdom = output.get(Calendar.DAY_OF_MONTH); 249 int outmon = output.get(Calendar.MONTH); 250 int inyear = input.get(Calendar.YEAR); 251 int indom = input.get(Calendar.DAY_OF_MONTH); 252 int inmon = input.get(Calendar.MONTH); 253 if (indom != outdom || inmon != outmon || inyear != outyear){ 254 fail("Test: '"+msg+"' Server="+longFormat.format(now.getTime()) 255 +". Failed to parse "+shortDate 256 +". Actual "+longFormat.format(output.getTime()) 257 +". Expected "+longFormat.format(input.getTime())); 258 } 259 } 260 261 public void testParseShortPastDates1() throws Exception { 262 GregorianCalendar now = new GregorianCalendar(2001, Calendar.MAY, 30, 12, 0); 263 checkShortParse("2001-5-30",now,now); // should always work 264 GregorianCalendar target = (GregorianCalendar) now.clone(); 265 target.add(Calendar.WEEK_OF_YEAR, -1); 266 checkShortParse("2001-5-30 -1 week",now,target); 267 target.add(Calendar.WEEK_OF_YEAR, -12); 268 checkShortParse("2001-5-30 -13 weeks",now,target); 269 target.add(Calendar.WEEK_OF_YEAR, -13); 270 checkShortParse("2001-5-30 -26 weeks",now,target); 271 } 272 273 public void testParseShortPastDates2() throws Exception { 274 GregorianCalendar now = new GregorianCalendar(2004, Calendar.AUGUST, 1, 12, 0); 275 checkShortParse("2004-8-1",now,now); // should always work 276 GregorianCalendar target = (GregorianCalendar) now.clone(); 277 target.add(Calendar.WEEK_OF_YEAR, -1); 278 checkShortParse("2004-8-1 -1 week",now,target); 279 target.add(Calendar.WEEK_OF_YEAR, -12); 280 checkShortParse("2004-8-1 -13 weeks",now,target); 281 target.add(Calendar.WEEK_OF_YEAR, -13); 282 checkShortParse("2004-8-1 -26 weeks",now,target); 283 } 284 285 // It has not yet been decided how to handle future dates, so skip these tests for now 286 287 // public void testParseShortFutureDates1() throws Exception { 288 // GregorianCalendar now = new GregorianCalendar(2001, Calendar.MAY, 30, 12, 0); 289 // checkShortParse("2001-5-30",now,now); // should always work 290 // GregorianCalendar target = (GregorianCalendar) now.clone(); 291 // target.add(Calendar.WEEK_OF_YEAR, 1); 292 // checkShortParse("2001-5-30 +1 week",now,target); 293 // target.add(Calendar.WEEK_OF_YEAR, 12); 294 // checkShortParse("2001-5-30 +13 weeks",now,target); 295 // target.add(Calendar.WEEK_OF_YEAR, 13); 296 // checkShortParse("2001-5-30 +26 weeks",now,target); 297 // } 298 299 // public void testParseShortFutureDates2() throws Exception { 300 // GregorianCalendar now = new GregorianCalendar(2004, Calendar.AUGUST, 1, 12, 0); 301 // checkShortParse("2004-8-1",now,now); // should always work 302 // GregorianCalendar target = (GregorianCalendar) now.clone(); 303 // target.add(Calendar.WEEK_OF_YEAR, 1); 304 // checkShortParse("2004-8-1 +1 week",now,target); 305 // target.add(Calendar.WEEK_OF_YEAR, 12); 306 // checkShortParse("2004-8-1 +13 weeks",now,target); 307 // target.add(Calendar.WEEK_OF_YEAR, 13); 308 // checkShortParse("2004-8-1 +26 weeks",now,target); 309 // } 310 311 // Test leap year if current year is a leap year 312 public void testFeb29IfLeapYear() throws Exception{ 313 GregorianCalendar now = new GregorianCalendar(); 314 final int thisYear = now.get(Calendar.YEAR); 315 if (now.isLeapYear(thisYear) && now.before(new GregorianCalendar(thisYear,Calendar.AUGUST,29))){ 316 GregorianCalendar target = new GregorianCalendar(thisYear,Calendar.FEBRUARY,29); 317 checkShortParse("Feb 29th",now,target); 318 } else { 319 System.out.println("Skipping Feb 29 test"); 320 } 321 } 322 323 // Test Feb 29 for a known leap year 324 public void testFeb29LeapYear() throws Exception{ 325 int year = 2000; // Use same year for current and short date 326 GregorianCalendar now = new GregorianCalendar(year, Calendar.APRIL, 1, 12, 0); 327 checkShortParse("Feb 29th 2000",now,new GregorianCalendar(year, Calendar.FEBRUARY,29)); 328 } 329 330 // Test Feb 29 for a known non-leap year - should fail 331 public void testFeb29NonLeapYear(){ 332 GregorianCalendar now = new GregorianCalendar(1999, Calendar.APRIL, 1, 12, 0); 333 // Note: we use a known leap year for the target date to avoid rounding up 334 try { 335 checkShortParse("Feb 29th 1999",now,new GregorianCalendar(2000, Calendar.FEBRUARY,29)); 336 fail("Should have failed to parse Feb 29th 1999"); 337 } catch (ParseException expected) { 338 } 339 } 340 341 public void testParseDec31Lenient() throws Exception { 342 GregorianCalendar now = new GregorianCalendar(2007, Calendar.DECEMBER, 30, 12, 0); 343 checkShortParse("2007-12-30",now,now); // should always work 344 GregorianCalendar target = (GregorianCalendar) now.clone(); 345 target.add(Calendar.DAY_OF_YEAR, +1); // tomorrow 346 checkShortParse("2007-12-31",now,target, true); 347 } 348 349 public void testParseJan01Lenient() throws Exception { 350 GregorianCalendar now = new GregorianCalendar(2007, Calendar.DECEMBER, 31, 12, 0); 351 checkShortParse("2007-12-31",now,now); // should always work 352 GregorianCalendar target = (GregorianCalendar) now.clone(); 353 target.add(Calendar.DAY_OF_YEAR, +1); // tomorrow 354 checkShortParse("2008-1-1",now,target, true); 355 } 356 357 public void testParseJan01() throws Exception { 358 GregorianCalendar now = new GregorianCalendar(2007, Calendar.JANUARY, 1, 12, 0); 359 checkShortParse("2007-01-01",now,now); // should always work 360 GregorianCalendar target = new GregorianCalendar(2006, Calendar.DECEMBER, 31, 12, 0); 361 checkShortParse("2006-12-31",now,target, true); 362 checkShortParse("2006-12-31",now,target, false); 363 } 364 365 /** 366 * Method suite. 367 * 368 * @return TestSuite 369 */ 370 public static TestSuite suite() 371 { 372 return(new TestSuite(FTPTimestampParserImplTest.class)); 373 } 374 375 376 377 }