Quarter.java

  1. package edu.ucsb.cs156.courses.models;

  2. import java.util.ArrayList;
  3. import java.util.List;
  4. import java.util.Objects;

  5. /**
  6.  * Represents a UCSB quarter. Allows easy conversion between QYY alphanumeric format (F19, W20, S20,
  7.  * M20) and YYYYQ numerical format (20194, 20201, 20202, 20203) as well as incrementing and
  8.  * decrementing.
  9.  */
  10. public class Quarter {

  11.   private int yyyyq; // YYYYQ where Q = 1, 2, 3 or 4

  12.   public Quarter(int yyyyq) {
  13.     setValue(yyyyq);
  14.   }

  15.   public int getValue() {
  16.     return this.yyyyq;
  17.   }

  18.   public void setValue(int yyyyq) {
  19.     if (invalidQtr(yyyyq))
  20.       throw new IllegalArgumentException(
  21.           "Quarter constructor requires a integer ending in 1,2,3 or 4");
  22.     this.yyyyq = yyyyq;
  23.   }

  24.   /**
  25.    * Construct a Quarter object from a string s, either in QYY or YYYYQ format. If s is of length
  26.    * three, QYY format is expected, if 5 then YYYYQ format is expected. Otherwise an
  27.    * IllegalArgumentException is thrown.
  28.    *
  29.    * @param s Quarter either in QYY or YYYYQ format
  30.    */
  31.   public Quarter(String s) {

  32.     switch (s.length()) {
  33.       case 3:
  34.         this.yyyyq = qyyToyyyyQ(s);
  35.         return;
  36.       case 5:
  37.         this.yyyyq = yyyyqToInt(s);
  38.         return;
  39.       default:
  40.         throw new IllegalArgumentException("Quarter should be in QYY or YYYYQ format");
  41.     }
  42.   }

  43.   public String getYY() {
  44.     return getYY(this.yyyyq);
  45.   }

  46.   public String getYYYY() {
  47.     return getYYYY(this.yyyyq);
  48.   }

  49.   public String getYYYYQ() {
  50.     return String.format("%d", this.yyyyq);
  51.   }

  52.   public String toString() {
  53.     return yyyyqToQyy(this.yyyyq);
  54.   }

  55.   public String getQ() {
  56.     return getQ(this.yyyyq);
  57.   }

  58.   private static boolean invalidQtr(int value) {
  59.     int index = value % 10;
  60.     return (index < 1) || (index > 4);
  61.   }

  62.   /**
  63.    * Advance to the next quarter, and return the value of that quarter as an int.
  64.    *
  65.    * @return the new getValue() for the quarter, i.e. quarter as in in yyyyq format
  66.    */
  67.   public int increment() {
  68.     int q = this.yyyyq % 10;
  69.     int yyyy = this.yyyyq / 10;

  70.     setValue((q == 4) ? (((yyyy + 1) * 10) + 1) : (this.yyyyq + 1));
  71.     return this.yyyyq;
  72.   }

  73.   /**
  74.    * Subtract one from current quarter, and return the value of that quarter as an int.
  75.    *
  76.    * @return the new getValue() for the quarter, i.e. quarter as in in yyyyq format
  77.    */
  78.   public int decrement() {
  79.     int q = this.yyyyq % 10;
  80.     int yyyy = this.yyyyq / 10;

  81.     setValue((q == 1) ? (((yyyy - 1) * 10) + 4) : (this.yyyyq - 1));
  82.     return this.yyyyq;
  83.   }

  84.   /**
  85.    * Convert yyyyq as string to int, throwing exception if format is incorrect
  86.    *
  87.    * @param yyyyq String in yyyyq format (e.g. 20194 for F19 (Fall 2019))
  88.    * @throws IllegalArgumentException
  89.    * @return int representation of quarter
  90.    */
  91.   public static int yyyyqToInt(String yyyyq) {
  92.     String errorMsg =
  93.         "Argument should be a string representation of a five digit integer yyyyq ending in 1,2,3 or 4";
  94.     int result = 0;
  95.     try {
  96.       result = Integer.parseInt(yyyyq);
  97.     } catch (Exception e) {
  98.       throw new IllegalArgumentException(errorMsg);
  99.     }
  100.     if (invalidQtr(result)) {
  101.       throw new IllegalArgumentException(errorMsg);
  102.     }
  103.     return result;
  104.   }

  105.   /**
  106.    * Convert yyyyq int format to Yqq String format throwing exception if format is incorrect
  107.    *
  108.    * @param yyyyq int (e.g. 20194 for Fall 2019
  109.    * @throws IllegalArgumentException
  110.    * @return Qyy representation (e.g. F19)
  111.    */
  112.   public static String yyyyqToQyy(int yyyyq) {
  113.     if (invalidQtr(yyyyq)) {
  114.       throw new IllegalArgumentException(
  115.           "Argument should be a five digit integer in qqqqy format ending in 1,2,3 or 4");
  116.     }
  117.     return String.format("%s%s", getQ(yyyyq), getYY(yyyyq));
  118.   }

  119.   /**
  120.    * Take yyyyq int format and return single character for quarter, either "W", "S", "M", or "F" for
  121.    * last digit 1, 2, 3, 4, respectively. Throw illegal argument exception if not in yyyyq format.
  122.    *
  123.    * @param yyyyq int (e.g. 20194 for Fall 2019)
  124.    * @throws IllegalArgumentException
  125.    * @return single char string for quarter (e.g. "F")
  126.    */
  127.   public static String getQ(int yyyyq) {
  128.     if (invalidQtr(yyyyq)) {
  129.       throw new IllegalArgumentException(
  130.           "Argument should be a five digit integer in qqqqy format ending in 1,2,3 or 4");
  131.     }
  132.     String[] quarters = new String[] {"W", "S", "M", "F"};
  133.     int index = yyyyq % 10;
  134.     return quarters[index - 1];
  135.   }

  136.   /**
  137.    * Take yyyyq int format and return two digit year as a String Throw illegal argument exception if
  138.    * not in yyyyq format.
  139.    *
  140.    * @param yyyyq int (e.g. 20194 for Fall 2019)
  141.    * @throws IllegalArgumentException
  142.    * @return two char string for year (e.g. "19")
  143.    */
  144.   public static String getYY(int yyyyq) {
  145.     if (invalidQtr(yyyyq)) {
  146.       throw new IllegalArgumentException(
  147.           "Argument should be a five digit integer in qqqqy format ending in 1,2,3 or 4");
  148.     }
  149.     return String.format("%02d", (yyyyq / 10) % 100);
  150.   }

  151.   /**
  152.    * Take yyyyq int format and return four digit year as a String Throw illegal argument exception
  153.    * if not in yyyyq format.
  154.    *
  155.    * @param yyyyq int (e.g. 20194 for Fall 2019)
  156.    * @throws IllegalArgumentException
  157.    * @return four char string for year (e.g. "2019")
  158.    */
  159.   public static String getYYYY(int yyyyq) {
  160.     if (invalidQtr(yyyyq)) {
  161.       throw new IllegalArgumentException(
  162.           "Argument should be a five digit integer in qqqqy format ending in 1,2,3 or 4");
  163.     }
  164.     return String.format("%04d", (yyyyq / 10));
  165.   }

  166.   public static int qyyToyyyyQ(String qyy) {
  167.     if (qyy.length() != 3) throw new IllegalArgumentException("Argument should be in QYY format");

  168.     char q = qyy.charAt(0);
  169.     String yy = qyy.substring(1, 3);
  170.     String legalQuarters = "WSMF";
  171.     int qInt = legalQuarters.indexOf(q) + 1;
  172.     if (invalidQtr(qInt)) {
  173.       throw new IllegalArgumentException("First char should be one of " + legalQuarters);
  174.     }
  175.     int yyInt = Integer.parseInt(yy);
  176.     int century = (yyInt > 50) ? 1900 : 2000;
  177.     return (century + yyInt) * 10 + qInt;
  178.   }

  179.   @Override
  180.   public boolean equals(Object o) {
  181.     if (o == this) return true;
  182.     if (!(o instanceof Quarter)) {
  183.       return false;
  184.     }
  185.     Quarter quarter = (Quarter) o;
  186.     return yyyyq == quarter.yyyyq;
  187.   }

  188.   @Override
  189.   public int hashCode() {
  190.     return Objects.hashCode(yyyyq);
  191.   }

  192.   /**
  193.    * return a list of Quarters starting with the start parameter and ending with the end parameter,
  194.    * inclusive.
  195.    *
  196.    * <p>The result will automatically go in chronological or reverse chronological order, depending
  197.    * on the order of the parameters.
  198.    *
  199.    * @param start
  200.    * @param end
  201.    * @return list of quarters in specified order
  202.    */
  203.   public static List<Quarter> quarterList(String start, String end) {
  204.     List<Quarter> result = new ArrayList<Quarter>();

  205.     int startInt = Quarter.yyyyqToInt(start);
  206.     int endInt = Quarter.yyyyqToInt(end);

  207.     if (startInt < endInt) {
  208.       for (Quarter iter = new Quarter(startInt); iter.getValue() <= endInt; iter.increment()) {
  209.         Quarter q = new Quarter(iter.getValue());
  210.         result.add(q);
  211.       }
  212.     }
  213.     if (startInt >= endInt) {
  214.       for (Quarter iter = new Quarter(startInt); iter.getValue() >= endInt; iter.decrement()) {
  215.         Quarter q = new Quarter(iter.getValue());
  216.         result.add(q);
  217.       }
  218.     }
  219.     return result;
  220.   }
  221. }