Hot questions for Using Joda-Time in calendar

Question:

I am developing Java GUI on Windows (Eclipse and SWT). I have a need for GUI user input of Dates (calendar widget) and then date/time (from data source) manipulation including dealing with different time zones.

I switched to Joda for better date/time manipulation as I believe this is better than standard SWT. However, as I understand it Joda does not support its own calendar widget.

Can anyone advise me how to do this that would be compatible with SWT and Joda?

I see that direct referencing:

private org.joda.time.DateTime jodaDateTime;
private swt.DateTime swtDateTime;

is an option but wondered what others there are.


Answer:

This is a simple enough conversion that you can just create utility methods to convert from and to the default SWT widget, e.g.

public class DateUtils {
  public static org.joda.time.DateTime makeJodaFromSWT(
                                org.eclipse.swt.widgets.DateTime widget) {
    return new DateTime(widget.getYear(),
                        widget.getMonth(),
                        widget.getDay(),
                        widget.getHours(),
                        widget.getMinutes(),
                        widget.getSeconds());
  }

  public static void updateSWTwithJoda(
                                org.eclipse.swt.widgets.DateTime widget,
                                org.joda.time.DateTime dateTime) {
    widget.setYear(dateTime.getYear());
    widget.setMonth(dateTime.getMonthOfYear());
    widget.setDay(dateTime.getDayOfMonth());
    widget.setHours(dateTime.getHourOfDay());
    widget.setMinutes(dateTime.getMinuteOfHour());
    widget.setSeconds(dateTime.getSecondOfMinute());
  }
}

I would need to know more about your project to come up with a "smarter" wrapping scheme than public static utility methods, but this should get you on the right track.

Question:

For example:

  • 5 February 2016 - first week,
  • 12 February 2016- second week,
  • 28 February 2016- last week

Answer:

Well, it is not quite clear how you define the week of month. For the following discussion, I assume you speak about weeks starting on Monday (like in ISO-8601-standard and in most European countries).

There are two possible ways of definition how to handle the start and end of month when counting the weeks.

Since the start of week on Monday is not necessarily the same as the first day of month, a week can start in previous month or belong to next month.

The JDK-classes SimpleDateFormat with its field pattern symbol w (and also the new JSR-310-field WeekFields.weekOfMonth()) use following strategy:

If the first day of month is falling on Monday to Thursday then the associated week has at least 4 days in current month and will be counted as week 1 otherwise as week 0 (zero). Consistently the last day of month will always use an incrementing number even if it belongs to first week of next month.

In contrast to that definition, CLDR date-time-pattern specification and ISO-8601 are almost silent about the details in context of week-of-month. However, these standards are not silent about the week-of-year where they describe another strategy. And CLDR explicitly says about week-of-month (section 8.4):

8.4 Week of Year

Values calculated for the Week of Year field range from 1 to 53 for the Gregorian calendar (they may have different ranges for other calendars). Week 1 for a year is the first week that contains at least the specified minimum number of days from that year. Weeks between week 1 of one year and week 1 of the following year are numbered sequentially from 2 to 52 or 53 (if needed). For example, January 1, 1998 was a Thursday. If the first day of the week is MONDAY and the minimum days in a week is 4 (these are the values reflecting ISO 8601 and many national standards), then week 1 of 1998 starts on December 29, 1997, and ends on January 4, 1998. However, if the first day of the week is SUNDAY, then week 1 of 1998 starts on January 4, 1998, and ends on January 10, 1998. The first three days of 1998 are then part of week 53 of 1997.

Values are similarly calculated for the Week of Month.

The difference between both strategies applied on the date 2016-02-29 will be:

  • week 5 according to JDK
  • week 1 according to CLDR/ISO

Now I present a solution for Joda-Time.

public static void main(String[] args) throws Throwable {

    System.out.println(getWeekOfMonth(false)); // CLDR/ISO-spec
    // 1 for today=2016-02-05
    // 2 for today=2016-02-12
    // 4 for today=2016-02-28
    // 1 for today=2016-02-29

    System.out.println(getWeekOfMonth(true)); // JDK-behaviour
    // 1 for today=2016-02-05
    // 2 for today=2016-02-12
    // 4 for today=2016-02-28
    // 5 for today=2016-02-29

}

private static int getWeekOfMonth(boolean bounded) {
    int weekOfMonth;
    LocalDate today = LocalDate.now();

    LocalDate first = today.dayOfMonth().withMinimumValue();
    int dowFirst = first.getDayOfWeek();

    if (dowFirst <= DateTimeConstants.THURSDAY) {
        // we are in week 1 and go to Monday as start of week
        first = first.minusDays(dowFirst - DateTimeConstants.MONDAY);

        // first try: we determine the week of current month
        weekOfMonth = Days.daysBetween(first, today).getDays() / 7 + 1;

        if (!bounded) {
            // edge case: are we in first week of next month?
            LocalDate next = first.plusMonths(1);
            int dowNext = next.getDayOfWeek();

            if (dowNext <= DateTimeConstants.THURSDAY) {
                next = next.minusDays(dowNext - DateTimeConstants.MONDAY);
                if (!next.isAfter(today)) {
                    weekOfMonth = 1;
                }
            }
        }
    } else if (bounded) {
        weekOfMonth = 0;
    } else {
        // we are in last week of previous month so let's check the start of previous month
        LocalDate previous = first.minusMonths(1);
        int dowPrevious = previous.getDayOfWeek();

        if (dowPrevious <= DateTimeConstants.THURSDAY) {
            previous = previous.minusDays(dowPrevious - DateTimeConstants.MONDAY);
        } else {
            previous = previous.plusDays(DateTimeConstants.MONDAY - dowPrevious + 7);
        }

        weekOfMonth = Days.daysBetween(previous, today).getDays() / 7 + 1;
    }

    return weekOfMonth;
}

I hope it is not too complex for you.


By the way, if you are interested how simple alternatives applicable on platforms older than Java-8 look like:

Time4J (my library)

private static int time4j(boolean bounded) { // supports both definitions
  PlainDate today = SystemClock.inLocalView().today(); // using system timezone
    return today.get(
          (bounded ? Weekmodel.ISO.boundedWeekOfMonth() : Weekmodel.ISO.weekOfMonth()));
}

Threeten-BP (backport of Java-8):

private static int threeten() { // only JDK-definition (code similar to Java-8)
    org.threeten.bp.LocalDate today = org.threeten.bp.LocalDate.now();
    return today.get(WeekFields.ISO.weekOfMonth());
}

Old JDK:

private static int oldJDK() { // only JDK-definition
    GregorianCalendar gcal = new GregorianCalendar();
    gcal.setMinimalDaysInFirstWeek(4);
    gcal.setFirstDayOfWeek(Calendar.MONDAY);
    return gcal.get(Calendar.WEEK_OF_MONTH);
}

As you can see, it is very easy with these alternatives to change the underlying week models to non-ISO cases (like US-weeks). If you want that in Joda-Time then I leave the task for you to rewrite the presented Joda-solution.

Update due to comment below about the topic:

So the whole thing is about day-of-week-in-month. Joda-Time does not support this element/field out of the box, too. Sorry. But you might be able to study the necessary algorithm for such a field used in other libraries.

A demo example in Time4J for modelling the rfc2445-rule mentioned in comment:

    PlainDate dtStart = PlainDate.of(2016, Month.FEBRUARY, 4);
    int count = 5;
    Weekday byday = Weekday.FRIDAY; // first
    int interval = 1;
    CalendarUnit frequency = CalendarUnit.MONTHS;

    List<PlainDate> sequence = new ArrayList<>(count);
    PlainDate wim = dtStart;
    for (int i = 0; i < count; i++) {
        wim = wim.with(PlainDate.WEEKDAY_IN_MONTH.setToFirst(byday));
        sequence.add(wim);
        wim = wim.with(PlainDate.DAY_OF_MONTH, 1).plus(interval, frequency);
    }
    if (!sequence.isEmpty() && !sequence.get(0).equals(dtStart)) {
        sequence.remove(0); // Not quite sure - do you need another condition?
    }
    System.out.println(sequence); // [2016-03-04, 2016-04-01, 2016-05-06, 2016-06-03]

In Java-8, there is also support via a specialized adjuster so you can easily transfer given demo example to Java-8 using java.time.LocalDate.

Question:

So I am using getAvailableZoneIds() method under java.time.ZoneId to fetch a list of available timezones.

I wanted to know if there is a way by which for a specific timezone eg- "America/Chicago" , I can find out the starting and ending date time detail of daylight saving time for a year. Like when does it start and ends. Gone though various classes including ZoneId ,ZonedDateTime, ZoneOffset,TimeZone available in java but not able to find any way by which I can fetch this details.

I tried the below code and gives output mentioned below

    ZoneId zoneId= ZoneId.of("America/Sao_Paulo");
    ZoneRules zoneRules = zoneId.getRules();
    System.out.println("previous Transition of DST ==>  " + zoneRules.previousTransition(Instant.now()));
    System.out.println("next Transition of DST ==>  " + zoneRules.nextTransition(Instant.now()));

Output:

previous Transition of DST ==>  Transition[Overlap at 2019-02-17T00:00-02:00 to -03:00]
next Transition of DST ==>  Transition[Gap at 2019-11-03T00:00-03:00 to -02:00]

But I need to find out for a particular year , what time it starts and what time does it end .


Answer:

For a particular year, you can give ZoneRules a year as part of specifying a moment. For any given moment, you can ask next or previous ZoneOffsetTransition.

Here is example code for Europe/Sofia time zone.

ZoneId zoneSofia = ZoneId.of( "Europe/Sofia" );
ZoneRules zoneRules = zoneSofia.getRules();

// Pick a moment, arbitrarily.
ZonedDateTime zdt = ZonedDateTime.of( 2019 , 10 , 15 , 10 , 0 , 0 , 0 , zoneSofia );
// Is DST in effect at that moment?
boolean isDst = zoneRules.isDaylightSavings( zdt.toInstant() );

// When are the closest offset transitions, previous (in the past), and next (in the future).
ZoneOffsetTransition previousTransition = zoneRules.previousTransition( zdt.toInstant() );
ZoneOffsetTransition nextTransition = zoneRules.nextTransition( zdt.toInstant() );

// When is the next transition happening in UTC? In Sofia time?
Instant nextTransitionInstant = nextTransition.getInstant();  // An `Instant`` is always in UTC, by definition.
ZonedDateTime nextTransactionZdt = nextTransitionInstant.atZone( zoneSofia ); // Same moment, same point on the timeline, different wall-clock time.
boolean isDstAfterTransition = zoneRules.isDaylightSavings( nextTransactionZdt.toInstant() );

Dump to console.

System.out.println( "zone = " + zoneSofia );
System.out.println( "zdt: " + zdt );
System.out.println( "isDst: " + isDst );
System.out.println( "previousTransition = " + previousTransition );
System.out.println( "nextTransition = " + nextTransition );
System.out.println( "nextTransitionInstant = " + nextTransitionInstant );
System.out.println( "nextTransactionZdt = " + nextTransactionZdt );
System.out.println( "isDstAfterTransition = " + isDstAfterTransition );

zone = Europe/Sofia

zdt: 2019-10-15T10:00+03:00[Europe/Sofia]

isDst: true

previousTransition = Transition[Gap at 2019-03-31T03:00+02:00 to +03:00]

nextTransition = Transition[Overlap at 2019-10-27T04:00+03:00 to +02:00]

nextTransitionInstant = 2019-10-27T01:00:00Z

nextTransactionZdt = 2019-10-27T03:00+02:00[Europe/Sofia]

isDstAfterTransition = false

We can see than the next transition in Europe/Sofia happens at the moment that would look like 4 AM while 3 hours ahead of UTC:

nextTransition = Transition[Overlap at 2019-10-27T04:00+03:00 to +02:00]

…but since we are doing a Daylight Saving Time (DST) "Fall back", we turn the hands of the clock back to 3 AM to be 2 hours ahead of UTC:

nextTransactionZdt = 2019-10-27T03:00+02:00[Europe/Sofia]

And we can see with isDstAfterTransition that at that moment we are no longer in DST.

Notice how, on this date of the 27th, the people of the the Sofia region experience the hour of 3-4 AM twice. This first 3-4 AM hour is at 3 hours ahead of UTC. The second 3-4 AM hour is at 2 hours ahead of UTC.

And, this means the day of the 27th runs 25 hours long rather than 24.

Question:

The question is in the heading really. What is the simplest way to do this?


Answer:

LocalDateTime jodaTime = LocalDateTime.now();
Calendar cal = Calendar.getInstance();
cal.setTime(jodaTime.toDate());

Question:

I'm developing a calendar app that needs to convert dates from other calendar systems to Gregorian dates. So I decided to use JodaTime.

Soon I found that different calendar systems are represented as Chronologys. And I looked through the list of Chronologys and see nothing about the Chinese Calendar system aka Lunar Calendar. Then I searched for "Lunar Calendar Joda Time" and I see IslamicChronology but I don't think that is the right one as it says on the page that this is used in Muslim countries.

So I'm not really sure how to use Chinese Calendar system using Joda Time. I also looked at another Stack Overflow post which suggests that I should use another library. However, Joda Time does not seem to have an interface to support that library which means I cannot be consistent. Some of the project I use Joda Time and some of it I don't. It just sounds weird to me.


Answer:

The only library which has support is ICU4J as far as I know. A strength of that library is extended i18n-support. Disadvantage is the API itself which is similar to java.util.Calendar.

Joda-Time does not support it and has no plans, too (since it is officially a "largely finished" project, the project owner recommends to implement any new calendars on the base of Threeten-Extra and welcomes any contributions of the community). It is also not so easy to implement it on the base of Joda-Time from the scratch - an important reason why Joda-Time does not support so many calendars. I18n is also not a strength.

My library Time4J will certainly implement it in the future including i18n-features (probably next year due to my limited working capabilities) so you might watch any activities there.

There is also a very old issue on JDK-bug-log. I think our hopes for the JDK implementing this calendar is tending towards zero. Sorry for the negative answer.

Update from 2018-03-07:

Still no news from Joda, but now my library Time4J finally supports the Chinese calendar offering a more accurate alternative to ICU4J.

Question:

I need to get the full days between two dates in java (the dates are given in Date type) . For example: 01/01/2015/12:00:00 - 01/02/2015/11:59:00 isn't a full day and i need to consider daylight savings. I know that jodatime lib does that but i reached the 65k method limit and i cant use jodatime lib. i tried the millisecond diff way and the while loop that uses the "before" method: Android/Java - Date Difference in days


Answer:

I manage to figure it out: i used some of this code - https://stackoverflow.com/a/28865648/3873513 and added some of mine:

     public static int calcDaysDiff(Date day1, Date day2) {
    Date d1 = new Date(day1.getTime());
    Date d2 = new Date(day2.getTime());
    Calendar date1 = Calendar.getInstance();
    date1.setTime(d1);
    Calendar date2 = Calendar.getInstance();
    date2.setTime(d2);
    //checks if the start date is later then the end date - gives 0 if it is
    if (date1.get(Calendar.YEAR) >= date2.get(Calendar.YEAR)) {
        if (date1.get(Calendar.DAY_OF_YEAR) >= date2.get(Calendar.DAY_OF_YEAR)) {
            return 0;
        }
    }
    //checks if there is a daylight saving change between the two dates

    int offset = calcOffset(d1, d2);

    if (date1.get(Calendar.YEAR) > date2.get(Calendar.YEAR)) {
        //swap them
        Calendar temp = date1;
        date1 = date2;
        date2 = temp;
    }

    return calcDaysDiffAux(date1, date2) + checkFullDay(date1, date2, offset);
}

// check if there is a 24 hour diff between the 2 dates including the daylight saving offset
public static int checkFullDay(Calendar day1, Calendar day2, int offset) {
    if (day1.get(Calendar.HOUR_OF_DAY) <= day2.get(Calendar.HOUR_OF_DAY) + offset) {
        return 0;
    }
    return -1;
}

// find the number of days between the 2 dates. check only the dates and not the hours
public static int calcDaysDiffAux(final Calendar day1, final Calendar day2) {
    Calendar dayOne = (Calendar) day1.clone(),
            dayTwo = (Calendar) day2.clone();

    if (dayOne.get(Calendar.YEAR) == dayTwo.get(Calendar.YEAR)) {
        return Math.abs(dayOne.get(Calendar.DAY_OF_YEAR) - dayTwo.get(Calendar.DAY_OF_YEAR));
    } else {

        int extraDays = 0;

        while (dayTwo.get(Calendar.YEAR) > dayOne.get(Calendar.YEAR)) {
            dayTwo.add(Calendar.YEAR, -1);
            // getActualMaximum() important for leap years
            extraDays += dayTwo.getActualMaximum(Calendar.DAY_OF_YEAR);
        }

        return extraDays - day1.get(Calendar.DAY_OF_YEAR) + day2.get(Calendar.DAY_OF_YEAR);
    }
}

Question:

import java.text.SimpleDateFormat;
import java.util.Calendar;
import java.util.Date;
import java.util.TimeZone;


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

        SimpleDateFormat dateFormatGmt = new SimpleDateFormat("yyyy-MM-dd'T'HH:mm:ss.SSS'Z'");

        Calendar presentCal = Calendar.getInstance(TimeZone.getTimeZone("GMT"));

        System.out.println("With Cal.."+dateFormatGmt.format(presentCal.getTime()));

        dateFormatGmt.setTimeZone(TimeZone.getTimeZone("GMT"));

        String currentDateTimeString = dateFormatGmt.format(new Date());

        System.out.println("With format.."+currentDateTimeString);

    }
}

OUTPUT:

With Cal..2014-11-14T12:50:23.400Z
With format..2014-11-14T07:20:23.400Z

Answer:

A date is an instant in time, and your TimeZone(s) are different between the two format calls. Change it to

    SimpleDateFormat dateFormatGmt = new SimpleDateFormat(
            "yyyy-MM-dd'T'HH:mm:ss.SSS'Z'");
    Calendar presentCal = Calendar.getInstance(TimeZone.getTimeZone("GMT"));
    dateFormatGmt.setTimeZone(TimeZone.getTimeZone("GMT")); // <-- here
    System.out.println("With Cal.."
            + dateFormatGmt.format(presentCal.getTime())); // <-- you use it 
                                                           // here.
    String currentDateTimeString = dateFormatGmt.format(new Date());
    System.out.println("With format.." + currentDateTimeString);

And I get the correct output here.

Question:

In my app I create an object that represents a high school class. This object holds 2 Calendar objects that represents the class's start and stop time each day. When a user creates an assignment I want to check if the current time is between the two times of any of the classes. If it is I know that the assignment was created during that class. Here is my current code that does not work because .getTime() returns a date that includes month, and day, while I would just like to compare hours, and minutes. SO how can I trim the returned dates to just include the time in day? Would this be easier with joda-time, and if so what classes should be used?

    public void checkTimeFrame() {
    time = Calendar.getInstance().getTime();
    ArrayList<SchoolClass> mList = mClassList;

    // Changes index if assignment falls under time frame of class
    for (int a = 0; a < mList.size(); a++) {
        if (mList.get(a).getStartTime() != null && mList.get(a).getEndTime() != null &&
                time.after(mList.get(a).getStartTime().getTime()) && time.before(mList.get(a)
                .getEndTime().getTime())) {
            index = a;
            updateClassEditText();
        }
    }
}

Answer:

JDK 8 Date-Time APIs are a good approach to solving these kinds of issues. Instead of Calendar , use LocalTime to store the start and end time of the class.

LocalTime now = LocalTime.now(ZoneId.systemDefault());
LocalTime start = mList.get(a).getStartTime();
LocalTime end = mList.get(a).getEndTime();
if(now.isAfter(start) && now.isBefore(end)){
    //do something
}

For Android, you can use The ThreeTenABP project which adapts the java.time APIs for Android.

Question:

I am developing an app called Infinite Calendar. And it is not infinite because everything has a limit and I am now finding this limit. I am using a CalendarView to display a calendar and JodaTime to help me to deal with dates.

My layout has only one view:

<CalendarView
    android:layout_width="match_parent"
    android:layout_height="match_parent"
    android:showWeekNumber="false"
    android:shownWeekCount="5"
    android:id="@+id/calendar">
</CalendarView>

And I am setting the maxDate and minDate by code:

calendar.setMinDate (0);
calendar.setMaxDate (9223372017014400000L);

And why did I use 9223372017014400000L? Because after some experiments, I found that is the maximum that JodaTime can get. And why didn't I set them in the layout file? Because I don't know the format. I tried a few times before but it kept throwing exceptions saying that the max is less than the min...

Now I think, that's so far so good. So I tried to set the date to some really big numbers:

Date date = new LocalDate (2000000, 1, 1).toDate ();
calendar.setDate (date.getTime ());

Actually I don't even know if I am doing this well or if I am just butchering this.

Now the strange thing happens. When I run the app, the CalendarView shows nothing but the month, the year and the days of week at the top! I searched and found another question similar to this:

Android CalendarView not displaying the days

But the answer told the OP to set the dimensions to match_parent, which I did apparently.

So here comes the questions, is there a special cause of this? If yes, what is it and how can I fix it? If no, what is the limit of CalendarView?

Update:

I tried to set the min date to default (not specifying that) and the max date to 1/1/2000000 and then set the date to 1/12000000. Now the calendar view shows the the date and other dates in 1999999! How weird!

Jon Skeet please help me!


Answer:

Judging by Google's own Calendar app on my phone which only goes up to the year 2038, I imagine their CalendarView suffers from a similar issue created by the Year 2038 Problem. This is probably the maximum date it supports, though I couldn't find any mention of that in the documentation.

You're probably better off looking into other solutions or writing your own, using 64 bit representations of the time - this should cover you for approximately 300 billion years...


An aside: JodaTime uses a 64 bit representation of the time, so all you have to do to get the max is convert 63 consecutive 1s to decimal which gets you 9223372036854775807. No experiments required and slightly higher than your result.

Question:


Answer:

You can use SimpleDateFormat in java since you know the date format.

Eg:

String testDate = "20170526T043000Z";

SimpleDateFormat simpleDateFormat = new SimpleDateFormat("yyyymmdd'T'HHmmss");
Date parsedDate = simpleDateFormat.parse(testDate);
System.out.println("Time in millis " + parsedDate.getTime());