I have a problem using java.util.Calendar and commons-lang DateUtil
The problem is that my test works correctly on local machine and fails on CloudBees. Seems like there are problems with locales, but I'm not sure.
Here is the code:
import org.testng.Assert;
import org.testng.annotations.BeforeClass;
import org.testng.annotations.Test;
//bla-bla-bla
public static final String TEST_DATE_AS_STRING = "13 10 2012 20:50:44";
public static final int MILLIS_IN_HOUR = 3600000;
private static final String LEAP_WEEK_DATE_AS_STRING = "31 10 2012 20:50:44";
private final SimpleDateFormat sdf = new SimpleDateFormat("dd MM yyyy HH:mm:ss");
#Test
public void getWeekDatePair() throws ParseException{
Date date = sdf.parse(TEST_DATE_AS_STRING);
DatePair dp = Util.DateTime.getWeekDatePair(date);
Assert.assertEquals(sdf.format(new Date(dp.getStart())), "08 10 2012 00:00:00");
//java.lang.AssertionError: expected [14 10 2012 00:00:00] but found [07 10 2012 00:00:00]
Assert.assertEquals(sdf.format(new Date(dp.getEnd())), "14 10 2012 00:00:00");
}
#Test
public void getLeapWeekDatePair() throws ParseException {
Date leapDate = sdf.parse(LEAP_WEEK_DATE_AS_STRING);
DatePair dp = Util.DateTime.getWeekDatePair(leapDate);
Assert.assertEquals(sdf.format(new Date(dp.getStart())), "29 10 2012 00:00:00");
//java.lang.AssertionError: expected [04 11 2012 00:00:00] but found [28 10 2012 00:00:00]
Assert.assertEquals(sdf.format(new Date(dp.getEnd())), "04 11 2012 00:00:00");
}
Here is failed test output:
java.lang.AssertionError: expected [04 11 2012 00:00:00] but found [28 10 2012 00:00:00]
at org.testng.Assert.fail(Assert.java:94)
at org.testng.Assert.failNotEquals(Assert.java:494)
at org.testng.Assert.assertEquals(Assert.java:123)
at org.testng.Assert.assertEquals(Assert.java:176)
at org.testng.Assert.assertEquals(Assert.java:186)
at ru.rating.utils.UtilDateTimeTest.getLeapWeekDatePair(UtilDateTimeTest.java:77)
expected [14 10 2012 00:00:00] but found [07 10 2012 00:00:00]
Stacktrace
java.lang.AssertionError: expected [14 10 2012 00:00:00] but found [07 10 2012 00:00:00]
at org.testng.Assert.fail(Assert.java:94)
at org.testng.Assert.failNotEquals(Assert.java:494)
at org.testng.Assert.assertEquals(Assert.java:123)
at org.testng.Assert.assertEquals(Assert.java:176)
at org.testng.Assert.assertEquals(Assert.java:186)
at ru.rating.utils.UtilDateTimeTest.getWeekDatePair(UtilDateTimeTest.java:69)
Here is implementation:
public static DatePair getWeekDatePair(){
return getWeekDatePair(new Date());
}
/**
* This is test method
* */
static DatePair getWeekDatePair( Date date){
Date truncDay = truncate(date.getTime(), Calendar.DAY_OF_MONTH);
Calendar calStart = getCalendarInstance(date, Calendar.DAY_OF_MONTH);
calStart.setTime(truncDay);
calStart.set(Calendar.DAY_OF_WEEK, Calendar.MONDAY);
Calendar calEnd = Calendar.getInstance();
calEnd.setTime(calStart.getTime());
calEnd.set(Calendar.DAY_OF_WEEK, Calendar.SUNDAY);
return new DatePair(calStart.getTime(), calEnd.getTime());
}
public static Date truncate(long date, int calField) {
Calendar cal = getCalendarInstance(new Date(date), calField);
cal = DateUtils.truncate(cal, calField);
return cal.getTime();
}
static Calendar getCalendarInstance(Date date, int calendarField){
//Calendar cal = Calendar.getInstance();
Calendar cal = new GregorianCalendar(Locale.ENGLISH);
cal.setTime(date);
if(calendarField!=Calendar.HOUR){
cal.set(Calendar.HOUR_OF_DAY, 0);
}
cal.set(Calendar.MINUTE, 0);
cal.set(Calendar.SECOND, 0);
cal.set(Calendar.MILLISECOND, 0);
return cal;
}
Although we are missing key piece of information here (how is Util.DateTime.getWeekDatePair(java.util.Date) implemented), I suspect that what you do there is instantiating java.util.Calendar using default Locale and then search for first day of the week.
My suspicion came from the fact, that you don't pass the Locale instance to the getWeekDatePair() method.
Now, what is the problem here? Well, the first day of the week depends on the Locale. Therefore when you instantiate the Calendar like this: Calendar.getInstance(), what you in fact do is: Calendar.getInstance(Locale.getDefault(Locale.Category.FORMAT). And of course the first day of the week may differ on two different machines, because the Locales may differ.
For example, first day of the week is Sunday in US, but Monday in Poland (I believe it is like that in Russia, isn't it?) Therefore if you do this test on two different machines, fist of which has Locale set to en-US and second to ru-RU, you may expect different results.
If it is only the problem of tests, you may just as well set default Locale and everything should be working just fine. However, please keep in mind that if you are testing web application, using default Locale is a bad thing, as it will return server Locale rather than the one that comes from web browser (or some user profile if you have one). Should this Locales differ, you might use something that is confusing for end user.
Edit
It is quite obvious why it happens from the implementation, and I gave you the hints previously. Consider this code:
Calendar cal = Calendar.getInstance(Locale.ENGLISH);
System.out.println(sdf.format(cal.getTime()));
// Cloning so that Calendar could be re-used
Calendar calEnd = (Calendar) cal.clone();
// Setting start of the week date
cal.set(Calendar.DAY_OF_WEEK, Calendar.MONDAY);
System.out.println(sdf.format(cal.getTime()));
calEnd.set(Calendar.DAY_OF_WEEK, Calendar.SUNDAY);
System.out.println(sdf.format(calEnd.getTime()));
This prints (correctly):
13 10 2012 00:00:00
08 10 2012 00:00:00
07 10 2012 00:00:00
Now, let's change the Calendar instantiation to:
Calendar cal = Calendar.getInstance(Locale.forLanguageTag("ru-RU"));
Voila, now you'll see:
13 10 2012 00:00:00
08 10 2012 00:00:00
14 10 2012 00:00:00
To see why this is the correct behavior, let's test this code as well:
System.out.println(cal.getFirstDayOfWeek());
For English Locale, it will return 1 (Sunday) as oppose to 2 (Monday) which is the result for Russian Locales. This code behaves correctly, as it returns Monday and Sunday from given week. The only "problem" is the fact, that week means something else all over the world.
As you can see, it has nothing to do with DateUtils, it is merely related to Calendar's behavior. And because of this code: Calendar cal = new GregorianCalendar(Locale.ENGLISH); the behavior should in fact be consistent, so you should always get an error no matter what machine you are testing the code on. It it is not, I really can't understand why.
Depending on what you are trying to achieve, it may make sense to add Locale as a parameter to your method (and instantiate Calendar accordingly), then write some tests covering few Locales (some Arabic Locale may also be interesting as nobody said first day of the week has to be either Sunday or Monday) or merely modifying test dates so that they match the correct Calendar's behavior.
Related
Why does java make us write more code?
e.g:
long time = 1509694664442L;
Calendar c = Calendar.getInstance();
System.out.println(c.getTime());
c.getTime().setTime(time); // doesn't update the calendar's time!
System.out.println(c.getTime());
// but this does
Date d = c.getTime();
d.setTime(time);
c.setTime(d);
System.out.println(c.getTime());
Output:
Fri Jun 22 16:30:06 BRT 2018
Fri Jun 22 16:30:06 BRT 2018
Fri Nov 03 04:37:44 BRT 2017
Can someone explain why this happens since calendar.getTime() doesn't return a new instance of java.util.Date?
Calendar.getTime() returns a new Date instance, so trying to change the value on the returned object is futile, it has no effect on the Calendar instance.
Take a look at the source:
public final Date getTime() {
return new Date(getTimeInMillis());
}
This clearly shows that changing anything on the returned date doesn't change the calendar object itself.
You could make the change, then call calendar.setTime with the modified date to go around this.
Why does java make us write more code than the normal?
Because you are using the old and outdated date and time classes. java.time, the modern Java date and time API, allows not only terser but also clearer code:
long time = 1_509_694_664_442L;
Instant i = Instant.ofEpochMilli(time);
System.out.println(i);
Or, since you asked for brevity:
Instant.ofEpochMilli( 1_509_694_664_442L ).toString()
Output:
2017-11-03T07:37:44.442Z
For something more human readable (for some of us :-)
ZoneId zone = ZoneId.of("America/Recife");
Locale brazil = Locale.forLanguageTag("pt-BR");
DateTimeFormatter formatter = DateTimeFormatter
.ofLocalizedDateTime(FormatStyle.FULL)
.withLocale(brazil);
String formatted = i.atZone(zone).format(formatter);
System.out.println(formatted);
sexta-feira, 3 de novembro de 2017 04:37:44 Horário Padrão de Brasília
Calendar.getTime()
…since calendar.getTime() doesn't return a new instance of java.util.Date?
It does exactly that. Here’s the implementation in JDK 10:
public final Date getTime() {
return new Date(getTimeInMillis());
}
Link
Oracle tutorial: Date Time explaining how to use java.time.
I want to get the max date of one month, that it's does't work in some day like the code, it's appear by condition as below
February has problem 2017 Feb Max is 28,If your computer's date is larger than 28, like 2017-03-29 or 2017-04-30
Date date = PrimeUtil.formatStringToTime("2017-02","yyyy-MM");
System.out.println(date);
Calendar c = Calendar.getInstance();
c.set(Calendar.YEAR,date.getYear());
c.set(Calendar.MONTH,date.getMonth());
c.set(Calendar.DAY_OF_MONTH,c.getActualMaximum(Calendar.DAY_OF_MONTH));
System.out.println(c.getTime());
I had your problem. The problem is that when you set the month, the day is shifted if needed.
I had a method:
public static Date getLastDayOfMounth(int month){
Calendar cTemp = Calendar.getInstance();
// here is where I need to set the day
cTemp.set(Calendar.MONTH, month);
cTemp.set(Calendar.DAY_OF_MONTH, cTemp.getActualMaximum(Calendar.DAY_OF_MONTH));
return cTemp.getTime();
}
I had a test unit for the method that test the method for each month, from 0 to 11. It has workt until today.
Today, 29 March 2018, the test case gone on error for february... why?
First cTemp = 29/03/2018 (today)
then cTemp.set(Calendar.MONTH, 1); is 29/02/2018 but this day does not esists, so became 01/03/2018
then cTemp.getActualMaximum(Calendar.DAY_OF_MONTH) is the last day of march.
I solved adding
cTemp.set(Calendar.DAY_OF_MONTH, 1);
before setting the month. So I start always from first day, avoid problem of skipping to the next month.
I solve the problem by myself,I flow the resource code find that If put February, and use c.set(Calendar.MONTH, date.getMonth()) that the GregorianCalendar have return a wrong time, it's maybe a bug of Calendar, I change my code like below, it will work well
Date date = PrimeUtil.formatStringToTime("2017-02","yyyy-MM");
System.out.println(date);
Calendar c = Calendar.getInstance();
c.setTime(date);
c.set(Calendar.DAY_OF_MONTH,c.getActualMaximum(c.DAY_OF_MONTH));
System.out.println(c.getTime());
the console will print:
Wed Feb 01 00:00:00 CST 2017
Tue Feb 28 00:00:00 CST 2017
It's looks like cause by the mythod c.set(Calendar.MONTH, date.getMonth());
I wasn't changing DAY_OF_MONTH, so as the current date is 29 March 2019, even I change month to February, but in 2019, February has only 28 days, so even after changing month to Feb, it was still considering March, to resolve this I have to set DAY_OF_MONTH to 1.
private fun getLengthOfMonth(year: Int, month: Int): Int {
val calendar = Calendar.getInstance()
calendar.set(Calendar.YEAR, year)
calendar.set(Calendar.MONTH, month - 1)
calendar.set(Calendar.DAY_OF_MONTH,1)
return calendar.getActualMaximum(Calendar.DAY_OF_MONTH)
}
Your case: the value of date is "2017-02", the value of c is: "2017-03-30"
date: 2017-02
c: 2017-03-30
c.set(Calendar.YEAR, date.getYear());
c.set(Calendar.MONTH, date.getMonth());
c.set(Calendar.DAY_OF_MONTH, c.getActualMaximum(Calendar.DAY_OF_MONTH));
=>
c.set(Calendar.YEAR, 2017);
c.set(Calendar.MONTH, 2);
c.set(Calendar.DAY_OF_MONTH, 31);
Here set 31 to DAY_OF_MONTH, Which could not get what you expected!
Another case:
the value of date is "2017-04", the value of c is: "2017-03-31"
date: 2017-04
c: 2017-05-31
When we set 31 to DAY_OF_MONTH, we will not get what we expected.
I'm getting inconsistent results when converting Dates to LocalDates, around the year 200. Using the following code to do the conversion:
private LocalDate toLocalDate(Date localDate)
{
return LocalDateTime.ofInstant(localDate.toInstant(), ZoneId.systemDefault()).toLocalDate();
}
My ZoneId.systemDefault() is Africa/Harare, which matches the CAT used in the test. The test case I run is
SimpleDateFormat simpleDateFormat = new SimpleDateFormat("EEE MMM dd HH:mm:ss zzz yyyy", Locale.US);
String dateString = "Tue Jan 01 00:00:00 CAT 200";
String dateString2 = "Tue Jan 01 00:00:00 CAT 201";
String dateString3 = "Wed Dec 31 00:00:00 CAT 200";
System.out.println(toLocalDate(simpleDateFormat.parse(dateString)));
System.out.println(toLocalDate(simpleDateFormat.parse(dateString2)));
System.out.println(toLocalDate(simpleDateFormat.parse(dateString3)));
My expected output for this would be
0200-01-01
0201-01-01
0200-12-31
Or, if not that, at least consistently incorrect values. The actual results are
0199-12-31
0201-01-01
0200-12-31
So it seems that the first one is being rolled back slightly, possibly the two hours corresponding to the CAT timezone? But why does this only happen on the one case? Doing the same experiment with the year 2000 does not produce the same error.
Stephen has provided an explanation in the comment. Basically, java.util.Date uses a calendar system which cuts over between the Julian calendar system and the Gregorian calendar system in 1582, skipping 10 days. So dates in 1582 or before will exhibit discrepancies - but the size of the discrepancy will vary over time - by 3 days every 400 years, on average. It so happens that between 200 and 400AD, you don't see this because that corresponds to when the discrepancy is 0.
Here's a short but complete program to demonstrate the problem:
import java.time.*;
import java.util.*;
public class Test {
public static void main(String[] args) throws Exception {
// Value obtained with Noda Time: should be 0199-12-31T22:00:00Z.
long millis = -55855792800000L;
Instant instant = Instant.ofEpochMilli(millis);
Date date = new Date(millis);
System.out.println(instant);
System.out.println(date);
}
}
Output on my machine:
0199-12-31T22:00:00Z
Tue Jan 01 22:00:00 GMT 200
This is all complicated by the problems in your initial code of assuming CAT and Africa/Harare are the same (at that point in time, Africa/Harare is regarded as having an offset of +02:10) and the incorrect day names in your strings - but it's the bug in Java which is causing the issue here.
I suggest you perform all your parsing using the java.time.format classes - then I'd hope you won't get this inconsistency.
I am getting date in 28/12/2013 format, but I need current date in a String format,
like
Thursday, August 21
so that I can set over TextView,
Explain a bit, if you think something is necessary, I am new to Android Development.
You can always refer to the documentation.
http://developer.android.com/reference/java/util/Date.html
In the documentation you will find this:
Calendar.get(Calendar.MONTH)
public int getMonth () #old do not use
Returns the gregorian calendar month for this Date object.
Calendar.get(Calendar.YEAR
public int getYear () #old do not use
Returns the gregorian calendar year since 1900 for this Date object.
Calendar.get(Calendar.DAY_OF_WEEK)
public int getDay () #old do not use
Returns the gregorian calendar day of the week for this Date object.
SimpleDateFormat sdf = new SimpleDateFormat("EEEE, MMM dd");
String formatedDate = sdf.format(your_date);
At the moment I haven't programmed any android app, I'll do that in the future. But I have found that here. It may soulve your problem hopefully.
DateFormat[] formats = new DateFormat[] {
DateFormat.getDateInstance(),
DateFormat.getDateTimeInstance(),
DateFormat.getTimeInstance(),
};
for (DateFormat df : formats) {
System.out.println(df.format(new Date(0)));
df.setTimeZone(TimeZone.getTimeZone("UTC"));
System.out.println(df.format(new Date(0)));
}
Produces this output when run on an en_US device in the America/Los_Angeles time zone:
Dec 31, 1969
Jan 1, 1970
Dec 31, 1969 4:00:00 PM
Jan 1, 1970 12:00:00 AM
4:00:00 PM
12:00:00 AM
This question already has answers here:
Closed 13 years ago.
Possible Duplicate:
How to determine the date one day prior to a given date in Java?
If I have a Java.Util.Date object, what is the best way to get an object representing the 24 hours in the past of it?
Using Java 1.6 java.util.Calendar.add:
public static Date subtractDay(Date date) {
Calendar cal = Calendar.getInstance();
cal.setTime(date);
cal.add(Calendar.DAY_OF_MONTH, -1);
return cal.getTime();
}
Others suggest using Joda Time, which is currently JSR 310, and should later be included in Java itself.
The important thing to remember is that the Date class should represent any points in time whilst the Calendar class is used to manipulate those points in time. Last of all, SimpleDateFormat will represent them as Strings.
So, the best way is to use the Calendar class to calculate the new Date for you. This will ensure that any vagaries (Daylight Saving, Leap Years and the like) are accounted for.
I'm assuming that you don't really want to find '24 Hours previous' but actually do want a new Date instance representing 'this time yesterday' - either way, you can ask the Calendar instance for a Date 24Hours prior to another or 1 Day prior.
The Daylight savings is a great example. The UK 'sprang forward' on the 26th March 2009. So, 1 day prior to 3.00a.m. on the 26.Mar.2009 should yield 3.00a.m. 25.Mar.2009 but 24 Hrs prior will yield 2.00a.m.
public class DateTests extends TestCase {
private static String EXPECTED_SUMMER_TIME = "2009.Mar.29 03:00:00";
private static String EXPECTED_SUMMER_TIME_LESS_DAY = "2009.Mar.28 03:00:00";
private static String EXPECTED_SUMMER_TIME_LESS_24_HRS = "2009.Mar.28 02:00:00";
private static String EXPECTED_SUMMER_TIME_LESS_FURTHER_24_HRS = "2009.Mar.27 02:00:00";
public void testSubtractDayOr24Hours() {
SimpleDateFormat formatter = new SimpleDateFormat("yyyy.MMM.dd HH:mm:SS");
Calendar calendar = Calendar.getInstance();
// Create our reference date, 3.00 a.m. on the day the clocks go forward (they 'went' forward at 02.00)
calendar.clear();
calendar.set(2009, 2, 29, 3, 0);
Date summerTime = calendar.getTime(); // Sun Mar 29 03:00:00 BST 2009
String formattedSummerTime = formatter.format(summerTime);
calendar.add(Calendar.DAY_OF_MONTH, -1);
// Our reference date less 'a day'
Date summerTimeLessADay = calendar.getTime(); // Sat Mar 28 03:00:00 GMT 2009
String formattedSummerTimeLessADay = formatter.format(summerTimeLessADay);
// reset the calendar instance to the reference day
calendar.setTime(summerTime);
// Our reference date less '24 hours' (is not quite 24 hours)
calendar.add(Calendar.HOUR, -24);
Date summerTimeLess24Hrs = calendar.getTime(); // Sat Mar 28 02:00:00 GMT 2009
String formattedSummerTimeLess24Hrs = formatter.format(summerTimeLess24Hrs);
// Third date shows that taking a further 24 hours from yields expected result
calendar.add(Calendar.HOUR, -24);
Date summerTimeLessFurther24Hrs = calendar.getTime(); // Fri Mar 27 02:00:00 GMT 2009
String formattedSummerTimeLessFurther24Hrs = formatter.format(summerTimeLessFurther24Hrs);
// reset the calendar once more to the day before
calendar.setTime(summerTimeLess24Hrs);
// Take a 'day' from the Sat will yield the same result as date 03 because Daylight Saving is not a factor
calendar.add(Calendar.DAY_OF_MONTH, -1);
Date summerTimeLessFurtherDay = calendar.getTime(); // Fri Mar 27 02:00:00 GMT 2009
String formattedSummerTimeLessFurtherDay = formatter.format(summerTimeLessFurtherDay);
assert(formattedSummerTime.equals(EXPECTED_SUMMER_TIME));
assert(formattedSummerTimeLessADay.equals(EXPECTED_SUMMER_TIME_LESS_DAY));
assert(formattedSummerTimeLess24Hrs.equals(EXPECTED_SUMMER_TIME_LESS_24_HRS));
assert(formattedSummerTimeLessFurther24Hrs.equals(EXPECTED_SUMMER_TIME_LESS_FURTHER_24_HRS));
// This last test proves that taking 24 hors vs. A Day usually yields the same result
assert(formattedSummerTimeLessFurther24Hrs.equals(formattedSummerTimeLessFurtherDay));
}
}
For testing date functions, wwwdot-timeanddate-dot-com is a great resource.
subtract 1000*60*60*24 from the time and create a new date.
Date yesterday = new Date(d.getTime() - (1000*60*60*24));
int dayInMs = 1000 * 60 * 60 * 24;
Date previousDay = new Date(olddate.getTime() - dayInMs);
Personally if there are a lot of time/date calculations, I'd go with Joda-time.