tags:

views:

205

answers:

4

Hi, I have the following java code to get the date of a specific week day:

SimpleDateFormat sdf = new SimpleDateFormat("dd/MM/yyyy");
Calendar cal = Calendar.getInstance();
cal.setTime(new Date());
cal.set(Calendar.YEAR, 2010);
cal.set(Calendar.WEEK_OF_YEAR, 37); //week 37 of year 2010
cal.set(Calendar.DAY_OF_WEEK, Calendar.THURSDAY);
System.out.println("date="+sdf.format(cal.getTime()));

When I put this code in a main(String[] args)method, like the following:

import java.util.*;
import java.lang.*;
import java.text.SimpleDateFormat;

public class test{
    public static void main(String[] args){

    /** get dates from a known week ID **/
    SimpleDateFormat sdf = new SimpleDateFormat("dd/MM/yyyy");
    Calendar cal = Calendar.getInstance();
    cal.setTime(new Date());
    cal.set(Calendar.YEAR, 2010);
    cal.set(Calendar.WEEK_OF_YEAR, 37);
    cal.set(Calendar.DAY_OF_WEEK, Calendar.THURSDAY);
    System.out.println("date="+sdf.format(cal.getTime()));
    }
}

and run it, I get the correct result which is date=09/09/2010. There is no problem.

HOWEVER...

When I put this code in a function of a Class, like the following:

Public Class MyService{

  MyService(){}
  ...
  ...
  public String getDateOfWeekDay(int weekId, int year, int weekDay){
      //weekId = 37; year=2010; weekDay = Calendar.THURSDAY

      SimpleDateFormat sdf = new SimpleDateFormat("dd/MM/yyyy");
      Calendar cal = Calendar.getInstance();
      cal.setTime(new Date());
      cal.set(Calendar.YEAR, year);
      cal.set(Calendar.WEEK_OF_YEAR, weekId);
      cal.set(Calendar.DAY_OF_WEEK, weekDay);

      //I am developing android app. so I use Log to printout
      logPrinter.println("date="+sdf.format(cal.getTime())); 

      return  sdf.format(cal.getTime());    
  }

}

in another class MainClass, MainClass will invoke this service function like following:

Public Class MainClass{

MyService myService = new MyService();
myService.getDateOfWeekDay(37,2010,Calendar.THURSDAY);

}

But the result returned is always the date of the current week's thursday (date=14/10/2010), not the Thursday of the week which I specified (week 37, year 2010, Thursday). WHY???? I use exactly the same java code to get the date of the specific week day, only used it in different ways, why the result is different???? I can not understand this...Anybody can explain to me??

+3  A: 

It's becoming a rote answer around here, but: Use JodaTime or one of the alternatives. Investing precious cycles into understanding the JDK calendar API is throwing good money after bad.

Related on SO: Should I use native date/time; I see (now) you're doing Android development, which I can't speak to, but on SO: JodaTime on Android?

If you're insisting on JDK, I did try your code, with a slight modification. First, I'd eliminate the cal.setTime(new Date()) -- it's not doing anything, and it's confusing since it might (seem to) correlate to always getting a date around "now," since new Date() gives you now. However, in your code, it still should be overridden by the following setX calls.

class DateTest
{
    DateTest(){}

    public String getDateOfWeekDay(int weekId, int year, int weekDay){

        Calendar cal = Calendar.getInstance();
        cal.set(Calendar.YEAR, year);
        cal.set(Calendar.WEEK_OF_YEAR, weekId);
        cal.set(Calendar.DAY_OF_WEEK, weekDay);

        SimpleDateFormat sdf = new SimpleDateFormat("dd/MM/yyyy");
        System.out.println("date="+sdf.format(cal.getTime())); 

        return  sdf.format(cal.getTime());    
    }

    public static void main(String args[])
    {
      DateTest dt = new DateTest();
      for (int weekCount = 1; weekCount <= 53; weekCount++) {
        dt.getDateOfWeekDay(weekCount,2010,Calendar.THURSDAY);
      }
    }

}

And I get exactly what I expect:

date=31/12/2009 
date=07/01/2010 
...
date=23/12/2010 
date=30/12/2010

Do you see different?

andersoj
Also related: http://stackoverflow.com/questions/3709870/how-do-libraries-in-different-programming-languages-handle-date-time-timestamp
andersoj
A: 
public class CalendarTest {

    public static String getDateOfWeekDay(int weekId, int year, int weekDay) {
        SimpleDateFormat sdf = new SimpleDateFormat("dd/MM/yyyy");
        Calendar cal = Calendar.getInstance();
        cal.setTime(new Date());
        cal.set(Calendar.YEAR, year);
        cal.set(Calendar.WEEK_OF_YEAR, weekId);
        cal.set(Calendar.DAY_OF_WEEK, weekDay);
        return sdf.format(cal.getTime());
    }

    public static void main(String[] args) {
        String someDay = getDateOfWeekDay(37,2010,Calendar.THURSDAY);
        System.out.println(someDay);
    }

}

Outputs 16/09/2010 As does

public class CalendarTest {
    public static void main(String[] args) {
        MyService s = new MyService();
        String someDay = s.getDateOfWeekDay(37, 2010, Calendar.THURSDAY);
        System.out.println(someDay);
    }
}

class MyService {
    public String getDateOfWeekDay(int weekId, int year, int weekDay) {
        SimpleDateFormat sdf = new SimpleDateFormat("dd/MM/yyyy");
        Calendar cal = Calendar.getInstance();
        cal.setTime(new Date());
        cal.set(Calendar.YEAR, year);
        cal.set(Calendar.WEEK_OF_YEAR, weekId);
        cal.set(Calendar.DAY_OF_WEEK, weekDay);
        return sdf.format(cal.getTime());
    }
}
RedGrittyBrick
As I said, by using the code in this way, there is no problem, the problem comes when I put the code in a function of one class, and another class invoke this function from the instance of the first class.
Mellon
Ok, now I am getting more confused. I made the two classes (MyService.java and CalendarTest.java), and compiled them, after I run CalendarTest, the result is correct. But why in my android project, it always give the date of current week's thursday??
Mellon
The Javadocs for Calendar will probably throw som light on this, there are traps for the unwary (see Javadoc quote in edit above)
RedGrittyBrick
+4  A: 

Yes, JodaTime is wonderful, but I'll bet you'd rather know what's wrong with your stuff.

Adding another JAR dependency might be a problem for a mobile device.

That method isn't good, IMO. The name is a misnomer, and it's doing too many things. You're creating a String, not a Date, and you're printing to a log. I'd recommend returning a Date and let clients worry about whether they want to turn it into a String. Logging is a cross-cutting concern.

Here's a class (and a JUnit test) that works. See what's different:

package util;

import java.util.Calendar;
import java.util.Date;

public class DateUtils
{
    public static Date getDateOfWeekDay(int weekId, int year, int weekDay)
    {
        Calendar cal = Calendar.getInstance();
        cal.set(Calendar.YEAR, year);
        cal.set(Calendar.WEEK_OF_YEAR, weekId);
        cal.set(Calendar.DAY_OF_WEEK, weekDay);
        cal.set(Calendar.HOUR, 0);
        cal.set(Calendar.MINUTE, 0);
        cal.set(Calendar.SECOND, 0);
        cal.set(Calendar.MILLISECOND, 0);

        return cal.getTime();
    }

}

JUnit test:

package util;

import org.junit.Assert;
import org.junit.Test;

import java.text.DateFormat;
import java.text.ParseException;
import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;

/**
 * DateUtilsTest
 * User: Michael
 * Date: 10/16/10
 * Time: 10:40 AM
 */
public class DateUtilsTest
{
    public static final DateFormat DEFAULT_FORMAT = new SimpleDateFormat("dd/MM/yyyy");

    @Test
    public void testNewYearsDayLastYear() throws ParseException
    {
        Date expected = DEFAULT_FORMAT.parse("1/1/2009");
        Date actual = DateUtils.getDateOfWeekDay(1, 2009, Calendar.THURSDAY);

        Assert.assertEquals(expected, actual);
    }

    @Test
    public void testTaxDay() throws ParseException
    {
        Date expected = DEFAULT_FORMAT.parse("15/4/2010");
        Date actual = DateUtils.getDateOfWeekDay(16, 2010, Calendar.THURSDAY);

        Assert.assertEquals(expected, actual);
    }

    @Test
    public void testGetDateOfWeekDay() throws ParseException
    {
        Date expected = DEFAULT_FORMAT.parse("16/10/2010");
        Calendar calendar = Calendar.getInstance();
        calendar.setTime(expected);
        int week = calendar.get(Calendar.WEEK_OF_YEAR);
        int year = calendar.get(Calendar.YEAR);
        int dayOfWeek = calendar.get(Calendar.DAY_OF_WEEK);

        Date actual = DateUtils.getDateOfWeekDay(week, year, dayOfWeek);

        Assert.assertEquals(expected, actual);
    }
}
duffymo
Heh, picking at my JodaTime suggestion, eh? I did (after knee-jerking to dis java.util.Calendar) provide a real answer. +1 for providing a JUnit test case. I'm moved to tears... ;-)
andersoj
@duffymo: I see you pulled out the `DateFormat` as a static field. That's my reaction too, but given the expected use cases, you should note that this makes the method non-threadsafe. Why a `DateFormat` can't be threadsafe (and why they don't retrofit it to be such) is beyond me.
andersoj
we know, we know, you like JodaTime :)P (so do I)
RD
You beat me with the real answer, andersoj (not initializing Calendar), but it's because I was writing that JUnit test and "loading new answers". Nice work. Good point about thread safety as well. I'd remove the DateFormat now. If the class isn't responsible for formatting anymore there's no need for it.
duffymo
A: 

Well, because these answers are less about answering your actual question than recommending to use JodaTime, I'll throw in my recommendation against JodaTime.

Date/Calendar as well as JodaTime have significant design flaws (quoting the authors) and if you plan to do work with time and date, use JSR-310.

JSR-310 is the replacement for both Date/Calendar and JodaTime which is more consistent and works better than both. There are considerations to include JSR-310 in Java 7.

Regardless if that works out or not, it is possible to use it today. It is just a jar file like JodaTime.

The benefits are:

  • Fixes the remaining problems of JodaTime
  • Consistent library design
  • Better naming conventions
  • Easier to read API
  • Everything immutable
  • Better usability from other programming languages

If you are interested in this topic:

soc