Quarter.java
package edu.ucsb.cs156.courses.models;
import java.util.ArrayList;
import java.util.List;
import java.util.Objects;
/**
* Represents a UCSB quarter. Allows easy conversion between QYY alphanumeric format (F19, W20, S20,
* M20) and YYYYQ numerical format (20194, 20201, 20202, 20203) as well as incrementing and
* decrementing.
*/
public class Quarter {
private int yyyyq; // YYYYQ where Q = 1, 2, 3 or 4
public Quarter(int yyyyq) {
setValue(yyyyq);
}
public int getValue() {
return this.yyyyq;
}
public void setValue(int yyyyq) {
if (invalidQtr(yyyyq))
throw new IllegalArgumentException(
"Quarter constructor requires a integer ending in 1,2,3 or 4");
this.yyyyq = yyyyq;
}
/**
* Construct a Quarter object from a string s, either in QYY or YYYYQ format. If s is of length
* three, QYY format is expected, if 5 then YYYYQ format is expected. Otherwise an
* IllegalArgumentException is thrown.
*
* @param s Quarter either in QYY or YYYYQ format
*/
public Quarter(String s) {
switch (s.length()) {
case 3:
this.yyyyq = qyyToyyyyQ(s);
return;
case 5:
this.yyyyq = yyyyqToInt(s);
return;
default:
throw new IllegalArgumentException("Quarter should be in QYY or YYYYQ format");
}
}
public String getYY() {
return getYY(this.yyyyq);
}
public String getYYYY() {
return getYYYY(this.yyyyq);
}
public String getYYYYQ() {
return String.format("%d", this.yyyyq);
}
public String toString() {
return yyyyqToQyy(this.yyyyq);
}
public String getQ() {
return getQ(this.yyyyq);
}
private static boolean invalidQtr(int value) {
int index = value % 10;
return (index < 1) || (index > 4);
}
/**
* Advance to the next quarter, and return the value of that quarter as an int.
*
* @return the new getValue() for the quarter, i.e. quarter as in in yyyyq format
*/
public int increment() {
int q = this.yyyyq % 10;
int yyyy = this.yyyyq / 10;
setValue((q == 4) ? (((yyyy + 1) * 10) + 1) : (this.yyyyq + 1));
return this.yyyyq;
}
/**
* Subtract one from current quarter, and return the value of that quarter as an int.
*
* @return the new getValue() for the quarter, i.e. quarter as in in yyyyq format
*/
public int decrement() {
int q = this.yyyyq % 10;
int yyyy = this.yyyyq / 10;
setValue((q == 1) ? (((yyyy - 1) * 10) + 4) : (this.yyyyq - 1));
return this.yyyyq;
}
/**
* Convert yyyyq as string to int, throwing exception if format is incorrect
*
* @param yyyyq String in yyyyq format (e.g. 20194 for F19 (Fall 2019))
* @throws IllegalArgumentException
* @return int representation of quarter
*/
public static int yyyyqToInt(String yyyyq) {
String errorMsg =
"Argument should be a string representation of a five digit integer yyyyq ending in 1,2,3 or 4";
int result = 0;
try {
result = Integer.parseInt(yyyyq);
} catch (Exception e) {
throw new IllegalArgumentException(errorMsg);
}
if (invalidQtr(result)) {
throw new IllegalArgumentException(errorMsg);
}
return result;
}
/**
* Convert yyyyq int format to Yqq String format throwing exception if format is incorrect
*
* @param yyyyq int (e.g. 20194 for Fall 2019
* @throws IllegalArgumentException
* @return Qyy representation (e.g. F19)
*/
public static String yyyyqToQyy(int yyyyq) {
if (invalidQtr(yyyyq)) {
throw new IllegalArgumentException(
"Argument should be a five digit integer in qqqqy format ending in 1,2,3 or 4");
}
return String.format("%s%s", getQ(yyyyq), getYY(yyyyq));
}
/**
* Take yyyyq int format and return single character for quarter, either "W", "S", "M", or "F" for
* last digit 1, 2, 3, 4, respectively. Throw illegal argument exception if not in yyyyq format.
*
* @param yyyyq int (e.g. 20194 for Fall 2019)
* @throws IllegalArgumentException
* @return single char string for quarter (e.g. "F")
*/
public static String getQ(int yyyyq) {
if (invalidQtr(yyyyq)) {
throw new IllegalArgumentException(
"Argument should be a five digit integer in qqqqy format ending in 1,2,3 or 4");
}
String[] quarters = new String[] {"W", "S", "M", "F"};
int index = yyyyq % 10;
return quarters[index - 1];
}
/**
* Take yyyyq int format and return two digit year as a String Throw illegal argument exception if
* not in yyyyq format.
*
* @param yyyyq int (e.g. 20194 for Fall 2019)
* @throws IllegalArgumentException
* @return two char string for year (e.g. "19")
*/
public static String getYY(int yyyyq) {
if (invalidQtr(yyyyq)) {
throw new IllegalArgumentException(
"Argument should be a five digit integer in qqqqy format ending in 1,2,3 or 4");
}
return String.format("%02d", (yyyyq / 10) % 100);
}
/**
* Take yyyyq int format and return four digit year as a String Throw illegal argument exception
* if not in yyyyq format.
*
* @param yyyyq int (e.g. 20194 for Fall 2019)
* @throws IllegalArgumentException
* @return four char string for year (e.g. "2019")
*/
public static String getYYYY(int yyyyq) {
if (invalidQtr(yyyyq)) {
throw new IllegalArgumentException(
"Argument should be a five digit integer in qqqqy format ending in 1,2,3 or 4");
}
return String.format("%04d", (yyyyq / 10));
}
public static int qyyToyyyyQ(String qyy) {
if (qyy.length() != 3) throw new IllegalArgumentException("Argument should be in QYY format");
char q = qyy.charAt(0);
String yy = qyy.substring(1, 3);
String legalQuarters = "WSMF";
int qInt = legalQuarters.indexOf(q) + 1;
if (invalidQtr(qInt)) {
throw new IllegalArgumentException("First char should be one of " + legalQuarters);
}
int yyInt = Integer.parseInt(yy);
int century = (yyInt > 50) ? 1900 : 2000;
return (century + yyInt) * 10 + qInt;
}
@Override
public boolean equals(Object o) {
if (o == this) return true;
if (!(o instanceof Quarter)) {
return false;
}
Quarter quarter = (Quarter) o;
return yyyyq == quarter.yyyyq;
}
@Override
public int hashCode() {
return Objects.hashCode(yyyyq);
}
/**
* return a list of Quarters starting with the start parameter and ending with the end parameter,
* inclusive.
*
* <p>The result will automatically go in chronological or reverse chronological order, depending
* on the order of the parameters.
*
* @param start
* @param end
* @return list of quarters in specified order
*/
public static List<Quarter> quarterList(String start, String end) {
List<Quarter> result = new ArrayList<Quarter>();
int startInt = Quarter.yyyyqToInt(start);
int endInt = Quarter.yyyyqToInt(end);
if (startInt < endInt) {
for (Quarter iter = new Quarter(startInt); iter.getValue() <= endInt; iter.increment()) {
Quarter q = new Quarter(iter.getValue());
result.add(q);
}
}
if (startInt >= endInt) {
for (Quarter iter = new Quarter(startInt); iter.getValue() >= endInt; iter.decrement()) {
Quarter q = new Quarter(iter.getValue());
result.add(q);
}
}
return result;
}
}