package com.gh.mygreen.xlsmapper.cellconvert;
import static com.gh.mygreen.xlsmapper.TestUtils.*;
import static org.hamcrest.Matchers.*;
import static org.junit.Assert.*;
import java.awt.Point;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.InputStream;
import java.io.OutputStream;
import java.sql.Time;
import java.sql.Timestamp;
import java.util.ArrayList;
import java.util.Calendar;
import java.util.Date;
import java.util.List;
import java.util.Map;
import org.junit.BeforeClass;
import org.junit.Test;
import com.gh.mygreen.xlsmapper.IsEmptyBuilder;
import com.gh.mygreen.xlsmapper.XlsMapper;
import com.gh.mygreen.xlsmapper.annotation.OverRecordOperate;
import com.gh.mygreen.xlsmapper.annotation.RecordTerminal;
import com.gh.mygreen.xlsmapper.annotation.XlsColumn;
import com.gh.mygreen.xlsmapper.annotation.XlsConverter;
import com.gh.mygreen.xlsmapper.annotation.XlsDateConverter;
import com.gh.mygreen.xlsmapper.annotation.XlsFormula;
import com.gh.mygreen.xlsmapper.annotation.XlsHint;
import com.gh.mygreen.xlsmapper.annotation.XlsHorizontalRecords;
import com.gh.mygreen.xlsmapper.annotation.XlsIsEmpty;
import com.gh.mygreen.xlsmapper.annotation.XlsSheet;
import com.gh.mygreen.xlsmapper.validation.SheetBindingErrors;
/**
* 日付、時刻型のタイプのチェック
*
* @version 1.5
* @since 0.5
* @author T.TSUCHIE
*
*/
public class DateTimeCellConverterTest {
/**
* テスト結果ファイルの出力ディレクトリ
*/
private static File OUT_DIR;
@BeforeClass
public static void setUpBeforeClass() throws Exception {
OUT_DIR = createOutDir();
}
@Test
public void test_load_date_time() throws Exception {
XlsMapper mapper = new XlsMapper();
mapper.getConig().setContinueTypeBindFailure(true);
try(InputStream in = new FileInputStream("src/test/data/convert.xlsx")) {
SheetBindingErrors errors = new SheetBindingErrors(DateTimeSheet.class);
DateTimeSheet sheet = mapper.load(in, DateTimeSheet.class, errors);
if(sheet.simpleRecords != null) {
for(SimpleRecord record : sheet.simpleRecords) {
assertRecord(record, errors);
}
}
if(sheet.formattedRecords != null) {
for(FormattedRecord record : sheet.formattedRecords) {
assertRecord(record, errors);
}
}
if(sheet.formulaRecords != null) {
for(FormulaRecord record : sheet.formulaRecords) {
assertRecord(record, errors);
}
}
}
}
private void assertRecord(final SimpleRecord record, final SheetBindingErrors errors) {
if(record.no == 1) {
// 空文字
assertThat(record.utilDate, is(nullValue()));
assertThat(record.sqlDate, is(nullValue()));
assertThat(record.sqlTime, is(nullValue()));
assertThat(record.timestamp, is(nullValue()));
} else if(record.no == 2) {
// Excelの日時型
assertThat(record.utilDate, is(toUtilDate(toTimestamp("2015-01-02 03:45:06.000"))));
assertThat(record.calendar, is(toCalendar(toTimestamp("2015-01-02 03:45:06.000"))));
assertThat(record.sqlDate, is(toSqlDate(toTimestamp("2015-01-02 03:45:06.000"))));
assertThat(record.sqlTime, is(toSqlTime(toTimestamp("2015-01-02 03:45:06.000"))));
assertThat(record.timestamp, is(toTimestamp("2015-01-02 03:45:06.000")));
} else if(record.no == 3) {
// 文字列型の場合
assertThat(record.utilDate, is(toUtilDate(toTimestamp("2015-01-02 03:45:06.000"))));
assertThat(record.calendar, is(toCalendar(toTimestamp("2015-01-02 03:45:06.000"))));
assertThat(record.sqlDate, is(toSqlDate(toTimestamp("2015-01-02 00:00:00.000"))));
assertThat(record.sqlTime, is(toSqlTime(toTimestamp("1970-01-01 03:45:06.000"))));
assertThat(record.timestamp, is(toTimestamp("2015-01-02 03:45:06.000")));
} else if(record.no == 4) {
// 文字列型の場合
assertThat(cellFieldError(errors, cellAddress(record.positions.get("utilDate"))).isTypeBindFailure(), is(true));
assertThat(cellFieldError(errors, cellAddress(record.positions.get("calendar"))).isTypeBindFailure(), is(true));
assertThat(cellFieldError(errors, cellAddress(record.positions.get("sqlDate"))).isTypeBindFailure(), is(true));
assertThat(cellFieldError(errors, cellAddress(record.positions.get("sqlTime"))).isTypeBindFailure(), is(true));
assertThat(cellFieldError(errors, cellAddress(record.positions.get("timestamp"))).isTypeBindFailure(), is(true));
} else if(record.no == 5) {
// Excelの日時型(日本語)
assertThat(record.utilDate, is(toUtilDate(toTimestamp("2015-01-02 03:45:06.000"))));
assertThat(record.calendar, is(toCalendar(toTimestamp("2015-01-02 03:45:06.000"))));
assertThat(record.sqlDate, is(toSqlDate(toTimestamp("2015-01-02 03:45:06.000"))));
assertThat(record.sqlTime, is(toSqlTime(toTimestamp("2015-01-02 03:45:06.000"))));
assertThat(record.timestamp, is(toTimestamp("2015-01-02 03:45:06.000")));
} else if(record.no == 6) {
// Excelの数値型
assertThat(record.utilDate, is(toUtilDate(toTimestamp("2015-01-02 03:45:06.000"))));
assertThat(record.calendar, is(toCalendar(toTimestamp("2015-01-02 03:45:06.000"))));
assertThat(record.sqlDate, is(toSqlDate(toTimestamp("2015-01-02 03:45:06.000"))));
assertThat(record.sqlTime, is(toSqlTime(toTimestamp("2015-01-02 03:45:06.000"))));
assertThat(record.timestamp, is(toTimestamp("2015-01-02 03:45:06.000")));
} else if(record.no == 7) {
// Excelの関数型
assertThat(record.utilDate, is(toUtilDate(toTimestamp("2015-01-02 03:45:06.000"))));
assertThat(record.calendar, is(toCalendar(toTimestamp("2015-01-02 03:45:06.000"))));
assertThat(record.sqlDate, is(toSqlDate(toTimestamp("2015-01-02 03:45:06.000"))));
assertThat(record.sqlTime, is(toSqlTime(toTimestamp("2015-01-02 03:45:06.000"))));
assertThat(record.timestamp, is(toTimestamp("2015-01-02 03:45:06.000")));
} else {
fail(String.format("not support test case. No=%d.", record.no));
}
}
private void assertRecord(final FormattedRecord record, final SheetBindingErrors errors) {
if(record.no == 1) {
// 空文字
assertThat(record.utilDate, is(toUtilDate(toTimestamp("2000-12-31 03:41:12"))));
assertThat(record.calendar, is(toCalendar(toTimestamp("2000-12-31 03:41:12"))));
assertThat(record.sqlDate, is(toSqlDate(toTimestamp("2000-12-31 00:00:00.000"))));
assertThat(cellFieldError(errors, cellAddress(record.positions.get("sqlTime"))).isTypeBindFailure(), is(true));
assertThat(record.timestamp, is(toTimestamp("1999-12-31 10:12:00.000")));
} else if(record.no == 2) {
// 文字列型の場合(正常)
assertThat(record.utilDate, is(toUtilDate(toTimestamp("2015-01-02 03:45:06.000"))));
assertThat(record.calendar, is(toCalendar(toTimestamp("2015-01-02 03:45:06.000"))));
assertThat(record.sqlDate, is(toSqlDate(toTimestamp("2015-12-31 00:00:00.000"))));
assertThat(record.sqlTime, is(toSqlTime(toTimestamp("1970-01-01 12:03:00.000"))));
assertThat(record.timestamp, is(toTimestamp("2015-01-02 03:45:00.000")));
} else if(record.no == 3) {
// 文字列型の場合(存在しない日付)
assertThat(cellFieldError(errors, cellAddress(record.positions.get("utilDate"))).isTypeBindFailure(), is(true));
assertThat(cellFieldError(errors, cellAddress(record.positions.get("calendar"))).isTypeBindFailure(), is(true));
assertThat(cellFieldError(errors, cellAddress(record.positions.get("sqlDate"))).isTypeBindFailure(), is(true));
assertThat(cellFieldError(errors, cellAddress(record.positions.get("sqlTime"))).isTypeBindFailure(), is(true));
assertThat(record.timestamp, is(toTimestamp("2016-01-02 03:45:00.000")));
} else {
fail(String.format("not support test case. No=%d.", record.no));
}
}
private void assertRecord(final FormulaRecord record, final SheetBindingErrors errors) {
if(record.no == 1) {
// 空文字
assertThat(record.utilDate, is(nullValue()));
assertThat(record.calendar, is(nullValue()));
assertThat(record.sqlDate, is(nullValue()));
assertThat(record.sqlTime, is(nullValue()));
assertThat(record.timestamp, is(nullValue()));
} else if(record.no == 2) {
// 文字列型の場合(正常)
assertThat(record.utilDate, is(toUtilDate(toTimestamp("1904-01-26 23:04:00.000"))));
assertThat(record.calendar, is(toCalendar(toTimestamp("1904-01-26 23:04:00.000"))));
assertThat(record.sqlDate, is(toSqlDate(toTimestamp("1904-01-26 23:04:00.000"))));
assertThat(record.sqlTime, is(toSqlTime(toTimestamp("1904-01-26 23:04:00.000"))));
assertThat(record.timestamp, is(toTimestamp("1904-01-26 23:04:00.000")));
} else {
fail(String.format("not support test case. No=%d.", record.no));
}
}
/**
* 日時型の書き込みテスト
*/
@Test
public void test_save_date_time() throws Exception {
// テストデータの作成
final DateTimeSheet outSheet = new DateTimeSheet();
// アノテーションなしのデータ型の作成
outSheet.add(new SimpleRecord()
.comment("空文字"));
outSheet.add(new SimpleRecord()
.utilDate(toUtilDate(toTimestamp("2015-01-02 03:45:06.000")))
.calendar(toCalendar(toTimestamp("2015-01-02 03:45:06.000")))
.sqlDate(toSqlDate(toTimestamp("2015-01-02 03:45:06.000")))
.sqlTime(toSqlTime(toTimestamp("2015-01-02 03:45:06.000")))
.timestamp(toTimestamp("2015-01-02 03:45:06.000"))
.comment("日時"));
// アノテーションありのデータ型の作成
outSheet.add(new FormattedRecord()
.comment("空文字"));
outSheet.add(new FormattedRecord()
.utilDate(toUtilDate(toTimestamp("2015-01-02 03:45:06.000")))
.calendar(toCalendar(toTimestamp("2015-01-02 03:45:06.000")))
.sqlDate(toSqlDate(toTimestamp("2015-01-02 03:45:06.000")))
.sqlTime(toSqlTime(toTimestamp("2015-01-02 03:45:06.000")))
.timestamp(toTimestamp("2015-01-02 03:45:06.000"))
.comment("日時"));
// 数式データの作成
outSheet.add(new FormulaRecord().comment("空文字"));
outSheet.add(new FormulaRecord().start(toTimestamp("2012-08-01 10:32:00.000")).end(toTimestamp("2016-08-28 09:36:00.000")).comment("日時の差"));
// ファイルへの書き込み
XlsMapper mapper = new XlsMapper();
mapper.getConig().setContinueTypeBindFailure(true);
File outFile = new File(OUT_DIR, "convert_datetime.xlsx");
try(InputStream template = new FileInputStream("src/test/data/convert_template.xlsx");
OutputStream out = new FileOutputStream(outFile)) {
mapper.save(template, out, outSheet);
}
// 書き込んだファイルを読み込み値の検証を行う。
try(InputStream in = new FileInputStream(outFile)) {
SheetBindingErrors errors = new SheetBindingErrors(DateTimeSheet.class);
DateTimeSheet sheet = mapper.load(in, DateTimeSheet.class, errors);
if(sheet.simpleRecords != null) {
assertThat(sheet.simpleRecords, hasSize(outSheet.simpleRecords.size()));
for(int i=0; i < sheet.simpleRecords.size(); i++) {
assertRecord(sheet.simpleRecords.get(i), outSheet.simpleRecords.get(i), errors);
}
}
if(sheet.formattedRecords != null) {
assertThat(sheet.formattedRecords, hasSize(outSheet.formattedRecords.size()));
for(int i=0; i < sheet.formattedRecords.size(); i++) {
assertRecord(sheet.formattedRecords.get(i), outSheet.formattedRecords.get(i), errors);
}
}
if(sheet.formulaRecords != null) {
assertThat(sheet.formulaRecords, hasSize(outSheet.formulaRecords.size()));
for(int i=0; i < sheet.formulaRecords.size(); i++) {
assertRecord(sheet.formulaRecords.get(i), outSheet.formulaRecords.get(i), errors);
}
}
}
}
/**
* 書き込んだレコードを検証するための
* @param inRecord
* @param outRecord
* @param errors
*/
private void assertRecord(final SimpleRecord inRecord, final SimpleRecord outRecord, final SheetBindingErrors errors) {
System.out.printf("%s - assertRecord::%s no=%d, comment=%s\n",
this.getClass().getSimpleName(), inRecord.getClass().getSimpleName(), inRecord.no, inRecord.comment);
assertThat(inRecord.no, is(outRecord.no));
assertThat(inRecord.utilDate, is(outRecord.utilDate));
assertThat(inRecord.calendar, is(outRecord.calendar));
assertThat(inRecord.sqlDate, is(outRecord.sqlDate));
assertThat(inRecord.sqlTime, is(outRecord.sqlTime));
assertThat(inRecord.timestamp, is(outRecord.timestamp));
assertThat(inRecord.comment, is(outRecord.comment));
}
/**
* 書き込んだレコードを検証するための
* @param inRecord
* @param outRecord
* @param errors
*/
private void assertRecord(final FormattedRecord inRecord, final FormattedRecord outRecord, final SheetBindingErrors errors) {
System.out.printf("%s - assertRecord::%s no=%d, comment=%s\n",
this.getClass().getSimpleName(), inRecord.getClass().getSimpleName(), inRecord.no, inRecord.comment);
if(inRecord.no == 1) {
assertThat(inRecord.no, is(outRecord.no));
assertThat(inRecord.utilDate, is(toUtilDate(toTimestamp("2000-12-31 03:41:12.000"))));
assertThat(inRecord.calendar, is(toCalendar(toTimestamp("2000-12-31 03:41:12.000"))));
assertThat(inRecord.sqlDate, is(toSqlDate(toTimestamp("2000-12-31 00:00:00.000"))));
assertThat(inRecord.sqlTime, is(nullValue()));
assertThat(inRecord.timestamp, is(toTimestamp("1999-12-31 10:12:00.000")));
assertThat(inRecord.comment, is(outRecord.comment));
} else {
assertThat(inRecord.no, is(outRecord.no));
assertThat(inRecord.utilDate, is(outRecord.utilDate));
assertThat(inRecord.calendar, is(outRecord.calendar));
assertThat(inRecord.sqlDate, is(outRecord.sqlDate));
assertThat(inRecord.sqlTime, is(outRecord.sqlTime));
assertThat(inRecord.timestamp, is(outRecord.timestamp));
assertThat(inRecord.comment, is(outRecord.comment));
}
}
/**
* 書き込んだレコードを検証するための
* @param inRecord
* @param outRecord
* @param errors
*/
private void assertRecord(final FormulaRecord inRecord, final FormulaRecord outRecord, final SheetBindingErrors errors) {
System.out.printf("%s - assertRecord::%s no=%d, comment=%s\n",
this.getClass().getSimpleName(), inRecord.getClass().getSimpleName(), inRecord.no, inRecord.comment);
if(inRecord.no == 1) {
assertThat(inRecord.no, is(outRecord.no));
assertThat(inRecord.utilDate, is(nullValue()));
assertThat(inRecord.calendar, is(nullValue()));
assertThat(inRecord.sqlDate, is(nullValue()));
assertThat(inRecord.sqlTime, is(nullValue()));
assertThat(inRecord.timestamp, is(nullValue()));
assertThat(inRecord.start, is(outRecord.start));
assertThat(inRecord.end, is(outRecord.end));
assertThat(inRecord.comment, is(outRecord.comment));
} else if(inRecord.no == 2) {
assertThat(inRecord.no, is(outRecord.no));
assertThat(inRecord.utilDate, is(toUtilDate(toTimestamp("1904-01-26 23:04:00.000"))));
assertThat(inRecord.calendar, is(toCalendar(toTimestamp("1904-01-26 23:04:00.000"))));
assertThat(inRecord.sqlDate, is(toSqlDate(toTimestamp("1904-01-26 23:04:00.000"))));
assertThat(inRecord.sqlTime, is(toSqlTime(toTimestamp("1904-01-26 23:04:00.000"))));
assertThat(inRecord.timestamp, is(toTimestamp("1904-01-26 23:04:00.000")));
assertThat(inRecord.start, is(outRecord.start));
assertThat(inRecord.end, is(outRecord.end));
assertThat(inRecord.comment, is(outRecord.comment));
}
}
@XlsSheet(name="日時型")
private static class DateTimeSheet {
@XlsHint(order=1)
@XlsHorizontalRecords(tableLabel="日時型(アノテーションなし)", terminal=RecordTerminal.Border, ignoreEmptyRecord=true,
overRecord=OverRecordOperate.Insert)
private List<SimpleRecord> simpleRecords;
@XlsHint(order=2)
@XlsHorizontalRecords(tableLabel="日付型(初期値、書式)", terminal=RecordTerminal.Border, ignoreEmptyRecord=true,
overRecord=OverRecordOperate.Insert)
private List<FormattedRecord> formattedRecords;
@XlsHint(order=3)
@XlsHorizontalRecords(tableLabel="日時型(数式)", terminal=RecordTerminal.Border, ignoreEmptyRecord=true,
overRecord=OverRecordOperate.Insert)
private List<FormulaRecord> formulaRecords;
/**
* レコードを追加する。noを自動的に付与する。
* @param record
* @return
*/
public DateTimeSheet add(SimpleRecord record) {
if(simpleRecords == null) {
this.simpleRecords = new ArrayList<>();
}
this.simpleRecords.add(record);
record.no(simpleRecords.size());
return this;
}
/**
* レコードを追加する。noを自動的に付与する。
* @param record
* @return
*/
public DateTimeSheet add(FormattedRecord record) {
if(formattedRecords == null) {
this.formattedRecords = new ArrayList<>();
}
this.formattedRecords.add(record);
record.no(formattedRecords.size());
return this;
}
/**
* レコードを追加する。noを自動的に付与する。
* @param record
* @return
*/
public DateTimeSheet add(FormulaRecord record) {
if(formulaRecords == null) {
this.formulaRecords = new ArrayList<>();
}
this.formulaRecords.add(record);
record.no(formulaRecords.size());
return this;
}
}
/**
* 日時型 - Converterのない単純な場合
*
*/
private static class SimpleRecord {
private Map<String, Point> positions;
private Map<String, String> labels;
@XlsColumn(columnName="No.")
private int no;
@XlsColumn(columnName="Dateクラス(util)")
private Date utilDate;
@XlsColumn(columnName="Calendarクラス")
private Calendar calendar;
@XlsColumn(columnName="Dateクラス(sql)")
private java.sql.Date sqlDate;
@XlsColumn(columnName="Timeクラス(sql)")
private Time sqlTime;
@XlsColumn(columnName="Timesamp(sql)")
private Timestamp timestamp;
@XlsColumn(columnName="備考")
private String comment;
@XlsIsEmpty
public boolean isEmpty() {
return IsEmptyBuilder.reflectionIsEmpty(this, "positions", "labels", "no");
}
public SimpleRecord no(int no) {
this.no = no;
return this;
}
public SimpleRecord utilDate(Date utilDate) {
this.utilDate = utilDate;
return this;
}
public SimpleRecord calendar(Calendar calendar) {
this.calendar = calendar;
return this;
}
public SimpleRecord sqlDate(java.sql.Date sqlDate) {
this.sqlDate = sqlDate;
return this;
}
public SimpleRecord sqlTime(Time sqlTime) {
this.sqlTime = sqlTime;
return this;
}
public SimpleRecord timestamp(Timestamp timestamp) {
this.timestamp = timestamp;
return this;
}
public SimpleRecord comment(String comment) {
this.comment = comment;
return this;
}
}
/**
* 日時型 - 初期値、書式
*
* @author T.TSUCHIE
*
*/
private static class FormattedRecord {
private Map<String, Point> positions;
private Map<String, String> labels;
@XlsColumn(columnName="No.")
private int no;
/** 初期値 */
@XlsConverter(defaultValue="2000-12-31 03:41:12")
@XlsColumn(columnName="Dateクラス(util)")
private Date utilDate;
@XlsConverter(defaultValue="2000-12-31 03:41:12")
@XlsColumn(columnName="Calendarクラス")
private Calendar calendar;
/** トリム */
@XlsConverter(defaultValue=" 2000-12-31 ", trim=true)
@XlsColumn(columnName="Dateクラス(sql)")
private java.sql.Date sqlDate;
/** 書式付き(初期値のフォーマットが不正) */
@XlsConverter(defaultValue="abc")
@XlsDateConverter(javaPattern="H時m分")
@XlsColumn(columnName="Timeクラス(sql)")
private Time sqlTime;
/** 書式付き(lenient=true) */
@XlsConverter(defaultValue="1999/12/31 10:12")
@XlsDateConverter(javaPattern="yyyy/M/d H:mm", lenient=true, locale="ja_JP", excelPattern="yyyy/m/d h:mm")
@XlsColumn(columnName="Timesamp(sql)")
private Timestamp timestamp;
@XlsColumn(columnName="備考")
private String comment;
@XlsIsEmpty
public boolean isEmpty() {
return IsEmptyBuilder.reflectionIsEmpty(this, "positions", "labels", "no");
}
public FormattedRecord no(int no) {
this.no = no;
return this;
}
public FormattedRecord utilDate(Date utilDate) {
this.utilDate = utilDate;
return this;
}
public FormattedRecord calendar(Calendar calendar) {
this.calendar = calendar;
return this;
}
public FormattedRecord sqlDate(java.sql.Date sqlDate) {
this.sqlDate = sqlDate;
return this;
}
public FormattedRecord sqlTime(Time sqlTime) {
this.sqlTime = sqlTime;
return this;
}
public FormattedRecord timestamp(Timestamp timestamp) {
this.timestamp = timestamp;
return this;
}
public FormattedRecord comment(String comment) {
this.comment = comment;
return this;
}
}
/**
* 日時型 - 数式
*
*/
private static class FormulaRecord {
private Map<String, Point> positions;
private Map<String, String> labels;
@XlsColumn(columnName="No.")
private int no;
@XlsColumn(columnName="Dateクラス(util)")
@XlsFormula(methodName="getFormula")
private Date utilDate;
@XlsColumn(columnName="Calendarクラス")
@XlsFormula(methodName="getFormula")
private Calendar calendar;
@XlsColumn(columnName="Dateクラス(sql)")
@XlsFormula(methodName="getFormula")
private java.sql.Date sqlDate;
@XlsColumn(columnName="Timeクラス(sql)")
@XlsFormula(methodName="getFormula")
private Time sqlTime;
@XlsColumn(columnName="Timesamp(sql)")
@XlsFormula(methodName="getFormula")
private Timestamp timestamp;
@XlsColumn(columnName="開始日時")
private Timestamp start;
@XlsColumn(columnName="終了日時")
private Timestamp end;
@XlsColumn(columnName="備考")
private String comment;
@XlsIsEmpty
public boolean isEmpty() {
return IsEmptyBuilder.reflectionIsEmpty(this, "positions", "labels", "no");
}
public String getFormula(final Point point) {
if(start == null || end == null) {
return null;
}
final int rowNumber = point.y + 1;
return String.format("$H%d-$G%d", rowNumber, rowNumber);
}
public FormulaRecord no(int no) {
this.no = no;
return this;
}
public FormulaRecord utilDate(Date utilDate) {
this.utilDate = utilDate;
return this;
}
public FormulaRecord calendar(Calendar calendar) {
this.calendar = calendar;
return this;
}
public FormulaRecord sqlDate(java.sql.Date sqlDate) {
this.sqlDate = sqlDate;
return this;
}
public FormulaRecord sqlTime(Time sqlTime) {
this.sqlTime = sqlTime;
return this;
}
public FormulaRecord timestamp(Timestamp timestamp) {
this.timestamp = timestamp;
return this;
}
public FormulaRecord start(Timestamp start) {
this.start = start;
return this;
}
public FormulaRecord end(Timestamp end) {
this.end = end;
return this;
}
public FormulaRecord comment(String comment) {
this.comment = comment;
return this;
}
}
}