Uploaded image for project: 'Jira Data Center'
  1. Jira Data Center
  2. JRASERVER-39837

Created vs. Resolved Gadget configured for "weekly" grouping calculates weeks using default locale rather than instance configuration

    XMLWordPrintable

Details

    Description

      Summary

      The Created vs. Resolved gadget is currently implemented to calculate week numbers for the "weekly" grouping option using java.util.Calendar instances configured with the default Locale. As a result, week numbers are calculated based on the physical location of the JIRA server, and not based on actual configuration of the instance.

      Specifically, if the Use ISO8601 standard in Date Picker setting has been activated on the Look and Feel admin page of a JIRA server located in North America, the Created vs. Resolved gadget will calculate week numbers that are mis-aligned with the instance configuration. This is because ISO8601 defines Monday-Sunday weeks, but North American locales will result in java.util.Calendar calculating Sunday-Saturday weeks. There may be other effects of this misalignment as well.

      This problem may also be the root cause for JRA-23398 and JRA-25061.

      Details

      The following code snippets show the implementation gap:

      com.atlassian.jira.charts.CreatedVsResolvedChart

      The method that generates the Created vs. Resolved chart uses DatePeriodStatisticsMapper objects to convert the Created and Resolved dates to the grouping values.

      line 402:
      final StatisticsMapper createdMapper = new DatePeriodStatisticsMapper(ChartUtil.getTimePeriodClass(periodName), DocumentConstants.ISSUE_CREATED, getTimeZone());
      
      line 413:
      StatisticsMapper resolvedMapper = new DatePeriodStatisticsMapper(ChartUtil.getTimePeriodClass(periodName), DocumentConstants.ISSUE_RESOLUTION_DATE, getTimeZone());
      

      com.atlassian.jira.charts.jfreechart.util.ChartUtil

      The ChartUtil class is used to provide a class that implements the TimePeriod interface, which is required by the DatePeriodStatisticsMapper constructor (shown above). ChartUtil returns the Class object for the appropriate JFreeChart class.

      import org.jfree.data.time.Week;
      
      ...
      
      public static Class getTimePeriodClass(ChartFactory.PeriodName periodName)
      {
          switch(periodName)
          {
              case daily:
                  return Day.class;
              case hourly:
                  return Hour.class;
              case weekly:
                  return Week.class;
              case monthly:
                  return Month.class;
              case quarterly:
                  return Quarter.class;
              case yearly:
                  return Year.class;
              default:
                  return Day.class;
          }
      }
      

      com.atlassian.jira.issue.statistics.DatePeriodStatisticsMapper

      DatePeriodStatisticsMapper creates an instance of the specified TimePeriod class, without providing a Locale object.

      public TimePeriod getValueFromLuceneField(String documentValue)
      {
          Date date = LuceneUtils.stringToDate(documentValue);
          if (date == null)
          {
              return null;
          }
          return RegularTimePeriod.createInstance(timePeriodClass, date, periodTimeZone);
      }
      

      org.jfree.data.time.Week

      Finally, the Week class provided by JFreeChart uses java.util.Calendar to calculate the year and week based on the provided Date instance. When the Week object is created without a provided Locale (which the DatePeriodStatisticsMapper does, shown above), the default Locale is used.

      import java.util.Calendar;
      
      ...
      
      public Week(Date time, TimeZone zone) {
          // defer argument checking...
          this(time, zone, Locale.getDefault());
      }
      
      ...
      
      public Week(Date time, TimeZone zone, Locale locale) {
      
          ...
      
          Calendar calendar = Calendar.getInstance(zone, locale);
          calendar.setTime(time);
      
          // sometimes the last few days of the year are considered to fall in
          // the *first* week of the following year.  Refer to the Javadocs for
          // GregorianCalendar.
          int tempWeek = calendar.get(Calendar.WEEK_OF_YEAR);
          if (tempWeek == 1
                  && calendar.get(Calendar.MONTH) == Calendar.DECEMBER) {
              this.week = 1;
              this.year = (short) (calendar.get(Calendar.YEAR) + 1);
          }
          else {
              this.week = (byte) Math.min(tempWeek, LAST_WEEK_IN_YEAR);
              int yyyy = calendar.get(Calendar.YEAR);
              // alternatively, sometimes the first few days of the year are
              // considered to fall in the *last* week of the previous year...
              if (calendar.get(Calendar.MONTH) == Calendar.JANUARY
                      && this.week >= 52) {
                  yyyy--;
              }
              this.year = (short) yyyy;
          }
          peg(calendar);
      }
      

      java.util.Calendar

      Finally, the documentation for java.util.Calendar describes that the Locale is used to determine how weeks are calculated, as shown by the definition of the getFirstDayOfWeek() method:

      public int getFirstDayOfWeek()

      Gets what the first day of the week is; e.g., SUNDAY in the U.S., MONDAY in France.

      Attachments

        Issue Links

          Activity

            People

              Unassigned Unassigned
              7ce5f856ba69 Kavian Moradhassel
              Votes:
              12 Vote for this issue
              Watchers:
              11 Start watching this issue

              Dates

                Created:
                Updated: