Redesign start end time when absent and present
diff --git a/commands/interceptor/duration.go b/commands/interceptor/duration.go
index 55c4b86..1be9974 100644
--- a/commands/interceptor/duration.go
+++ b/commands/interceptor/duration.go
@@ -26,32 +26,31 @@
 )
 
 var stepFormats = map[schema.Step]string{
-	schema.StepSecond: "2006-01-02 1504",
+	schema.StepSecond: "2006-01-02 150400",
 	schema.StepMinute: "2006-01-02 1504",
 	schema.StepHour:   "2006-01-02 15",
 	schema.StepDay:    "2006-01-02",
-	schema.StepMonth:  "2006-01-02",
+	schema.StepMonth:  "2006-01",
 }
 
-var supportedTimeLayouts = []string{
-	"2006-01-02 150400",
-	"2006-01-02 1504",
-	"2006-01-02 15",
-	"2006-01-02",
-	"2006-01",
+var stepDuration = map[schema.Step]time.Duration{
+	schema.StepSecond: time.Second,
+	schema.StepMinute: time.Minute,
+	schema.StepHour:   time.Hour,
+	schema.StepDay:    time.Hour * 24,
+	schema.StepMonth:  time.Hour * 24 * 30,
 }
 
-func tryParseTime(unparsed string, parsed *time.Time) error {
+func tryParseTime(unparsed string) (schema.Step, time.Time, error) {
 	var possibleError error = nil
-	for _, layout := range supportedTimeLayouts {
+	for step, layout := range stepFormats {
 		t, err := time.Parse(layout, unparsed)
 		if err == nil {
-			*parsed = t
-			return nil
+			return step, t, nil
 		}
 		possibleError = err
 	}
-	return possibleError
+	return schema.StepSecond, time.Time{}, possibleError
 }
 
 // DurationInterceptor sets the duration if absent, and formats it accordingly,
@@ -80,42 +79,62 @@
 // in the format, (e.g. 2019-11-09 1001), so they'll be considered as UTC-based,
 // and generate the missing `start`(`end`) based on the same timezone, UTC
 func ParseDuration(start string, end string) (time.Time, time.Time, schema.Step) {
+	logger.Log.Debugln("Start time:", start, "end time:", end)
+
 	now := time.Now().UTC()
 
-	startTime := now
-	endTime := now
-	logger.Log.Debugln("Start time:", start, "end time:", end)
-	if len(start) == 0 && len(end) == 0 { // both absent
-		startTime = now.Add(-30 * time.Minute)
-		endTime = now
-	} else if len(end) == 0 { // start is present
-		if err := tryParseTime(start, &startTime); err != nil {
+	// both are absent
+	if len(start) == 0 && len(end) == 0 {
+		return now.Add(-30 * time.Minute), now, schema.StepMinute
+	}
+
+	var startTime, endTime time.Time
+	var step schema.Step
+	var err error
+
+	// both are present
+	if len(start) > 0 && len(end) > 0 {
+		start, end = AlignPrecision(start, end)
+
+		if step, startTime, err = tryParseTime(start); err != nil {
 			logger.Log.Fatalln("Unsupported time format:", start, err)
 		}
-	} else if len(start) == 0 { // end is present
-		if err := tryParseTime(end, &endTime); err != nil {
+		if step, endTime, err = tryParseTime(end); err != nil {
 			logger.Log.Fatalln("Unsupported time format:", end, err)
 		}
-	} else { // both are present
-		if err := tryParseTime(start, &startTime); err != nil {
+
+		return startTime, endTime, step
+	}
+
+	// end is absent
+	if len(end) == 0 {
+		if step, startTime, err = tryParseTime(start); err != nil {
 			logger.Log.Fatalln("Unsupported time format:", start, err)
 		}
-		if err := tryParseTime(end, &endTime); err != nil {
+		return startTime, startTime.Add(30 * stepDuration[step]), step
+	}
+
+	// start is present
+	if len(start) == 0 {
+		if step, endTime, err = tryParseTime(end); err != nil {
 			logger.Log.Fatalln("Unsupported time format:", end, err)
 		}
+		return endTime.Add(-30 * stepDuration[step]), endTime, step
 	}
-	duration := endTime.Sub(startTime)
-	step := schema.StepSecond
-	if duration.Hours() >= 24*30 { // time range > 1 month
-		step = schema.StepMonth
-	} else if duration.Hours() > 24 { // time range > 1 day
-		step = schema.StepDay
-	} else if duration.Minutes() > 60 { // time range > 1 hour
-		step = schema.StepHour
-	} else if duration.Seconds() > 60 { // time range > 1 minute
-		step = schema.StepMinute
-	} else if duration.Seconds() <= 0 { // illegal
-		logger.Log.Fatalln("end time must be later than start time, end time:", endTime, ", start time:", startTime)
-	}
+
+	logger.Log.Fatalln("Should never happen")
+
 	return startTime, endTime, step
 }
+
+// AlignPrecision aligns the two time strings to same precision
+// by truncating the more precise one
+func AlignPrecision(start string, end string) (string, string) {
+	if len(start) < len(end) {
+		return start, end[0:len(start)]
+	}
+	if len(start) > len(end) {
+		return start[0:len(end)], end
+	}
+	return start, end
+}
diff --git a/commands/interceptor/duration_test.go b/commands/interceptor/duration_test.go
index 7879cfd..c9b587d 100644
--- a/commands/interceptor/duration_test.go
+++ b/commands/interceptor/duration_test.go
@@ -44,20 +44,20 @@
 			name: "Should set current time if start is absent",
 			args: args{
 				start: "",
-				end:   now.Add(10 * time.Minute).Format(timeFormat),
+				end:   now.Format(timeFormat),
 			},
-			wantedStartTime: now,
-			wantedEndTime:   now.Add(10 * time.Minute),
+			wantedStartTime: now.Add(-30 * time.Minute),
+			wantedEndTime:   now,
 			wantedStep:      schema.StepMinute,
 		},
 		{
 			name: "Should set current time if end is absent",
 			args: args{
-				start: now.Add(-10 * time.Minute).Format(timeFormat),
+				start: now.Format(timeFormat),
 				end:   "",
 			},
-			wantedStartTime: now.Add(-10 * time.Minute),
-			wantedEndTime:   now,
+			wantedStartTime: now,
+			wantedEndTime:   now.Add(30 * time.Minute),
 			wantedStep:      schema.StepMinute,
 		},
 		{
@@ -96,3 +96,55 @@
 		})
 	}
 }
+
+func TestAlignPrecision(t *testing.T) {
+	type args struct {
+		start string
+		end   string
+	}
+	tests := []struct {
+		name        string
+		args        args
+		wantedStart string
+		wantedEnd   string
+	}{
+		{
+			name: "Should keep both when same precision",
+			args: args{
+				start: "2019-01-01",
+				end:   "2019-01-01",
+			},
+			wantedStart: "2019-01-01",
+			wantedEnd:   "2019-01-01",
+		},
+		{
+			name: "Should truncate start when it's less precise",
+			args: args{
+				start: "2019-01-01 1200",
+				end:   "2019-01-01",
+			},
+			wantedStart: "2019-01-01",
+			wantedEnd:   "2019-01-01",
+		},
+		{
+			name: "Should truncate end when it's less precise",
+			args: args{
+				start: "2019-01-01",
+				end:   "2019-01-01 1200",
+			},
+			wantedStart: "2019-01-01",
+			wantedEnd:   "2019-01-01",
+		},
+	}
+	for _, tt := range tests {
+		t.Run(tt.name, func(t *testing.T) {
+			gotStart, gotEnd := AlignPrecision(tt.args.start, tt.args.end)
+			if gotStart != tt.wantedStart {
+				t.Errorf("AlignPrecision() gotStart = %v, wantedStart %v", gotStart, tt.wantedStart)
+			}
+			if gotEnd != tt.wantedEnd {
+				t.Errorf("AlignPrecision() gotEnd = %v, wantedStart %v", gotEnd, tt.wantedEnd)
+			}
+		})
+	}
+}