Thanks to visit codestin.com
Credit goes to github.com

Skip to content
This repository was archived by the owner on Jul 11, 2024. It is now read-only.
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
33 changes: 29 additions & 4 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
# dtdiff
Golang package and command line tool to return or output the difference between date, time or duration

`dtdiff` allows you to answer two types of questions:
`dtdiff` allows you to answer three types of questions:

1. What is the duration between two different dates and/or times?
* start and end can be in various formats, such as:
Expand All @@ -15,6 +15,7 @@ Golang package and command line tool to return or output the difference between
* * 3 weeks 4 days 5 hours *(or 3W4D5h)*
* * 8 months 7 days 6 hours 5 minutes 4 seconds *(or 8M7D6h5m4s)*
* * 1 year 2 months 3 days 4 hours 5 minutes 6 second 7 milliseconds 8 microseconds 9 nanoseconds *(or 1Y2M3D4h5m6s7ms8us9ns)*
3. Similar to question two, but repeats a period multiple times or until a certain datetime is encountered.

## Installation

Expand Down Expand Up @@ -51,11 +52,20 @@ fmt.Println(future) // 2024-01-02 01:02:03
past, _ := dtdiff.Sub(from, period)
fmt.Println(past) // 2023-12-30 22:57:57

// example 3 duration with five intervals
// example 3 - duration with five intervals
from := "2024-06-28T04:25:41Z"
period := "1M1W1h1m2s"
recurrence := 5
all, _ := dtdiff.AddWithRecurrence(from, period, recurrence)
all, _ := dtdiff.AddWithRecurrence(from, period, recurrence) // can also use SubWithRecurrence
for _, a := range all {
fmt.Println(a)
}

// example 4 - repeat interval until a datetime is encountered
from := "2024-06-28T04:25:41Z"
period := "1M1W1h1m2s"
until := "2025-01-01 09:30:51"
all, err := dtdiff.AddUntil(from, until, period) // can also use SubUntil
for _, a := range all {
fmt.Println(a)
}
Expand Down Expand Up @@ -88,8 +98,9 @@ Flag Group 1 (mutually exclusive with Flag Group 2):
Flag Group 2:
-A, --add string add: a duration to use with -F, such as '1 day 2 hours 3 seconds'
-F, --from string a base date, time or datetime to use with -A or -S
-R, --recurrence recurrence interval
-R, --recurrence int repeat period this number of times (mutually exclusive with -U)
-S, --sub string subtract: a duration to use with -F, such as '5 months 4 weeks 3 days'
-U, --until string repeat period until date/time is exceeded

Durations:
years months weeks days
Expand All @@ -100,6 +111,7 @@ Brief Durations: (dates are upper, times are lower)
Y M W D
h m s ms us ns
examples: 1Y2M3W4D5h6m7s8ms9us1ns, "1Y 2M 3W 4D 5h 6m 7s 8ms 9us 1ns"

```

**Note:** The `-i` switch can accept two different types of input:
Expand Down Expand Up @@ -178,6 +190,19 @@ $ dtdiff -F 2024-01-01 -A "1 hour 30 minutes 45 seconds"
# can also use "milliseconds", "microseconds"
$ dtdiff -F "2024-01-02 01:02:03" -S "1 day 1 hour 2 minutes 3 seconds"
2024-01-01 00:00:00 -0500 EST

# output multiple occurrences: add 5 weeks, for 3 intervals
$ dtdiff -F "2024-01-02" -A "5W" -R 3
2024-02-06 00:00:00 -0500 EST
2024-03-12 00:00:00 -0400 EDT
2024-04-16 00:00:00 -0400 EDT

# repeat until a certain datetime is encountered: subtract 5 minutes until 15:00
$ dtdiff -F 15:20 -S 5m -U 15:00
2024-06-30 15:15:00 -0400 EDT
2024-06-30 15:10:00 -0400 EDT
2024-06-30 15:05:00 -0400 EDT
2024-06-30 15:00:00 -0400 EDT
```

## LICENSE
Expand Down
44 changes: 42 additions & 2 deletions cmd/dtdiff/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -29,7 +29,7 @@ Flag Group 1 (mutually exclusive with Flag Group 2):
{{FlagUsagesCustom .LocalFlags "start" "end" "stdin" "brief" | trimTrailingWhitespaces}}

Flag Group 2:
{{FlagUsagesCustom .LocalFlags "from" "add" "sub" "recurrence" | trimTrailingWhitespaces}}
{{FlagUsagesCustom .LocalFlags "from" "add" "sub" "recurrence" "until" | trimTrailingWhitespaces}}

Durations:
years months weeks days
Expand All @@ -56,6 +56,7 @@ var (
add string
sub string
recurrence int
until string
noNewline bool
readFromStdin bool
brief bool
Expand All @@ -74,6 +75,8 @@ var (
if len(from) > 0 && len(add) > 0 {
if recurrence > 0 {
computeAddSubWithRecurrence(from, add, 0, recurrence)
} else if len(until) > 0 {
computeUntil(from, until, add, 0)
} else {
computeAddSub(from, add, 0)
}
Expand All @@ -82,6 +85,8 @@ var (
if len(from) > 0 && len(sub) > 0 {
if recurrence > 0 {
computeAddSubWithRecurrence(from, sub, 1, recurrence)
} else if len(until) > 0 {
computeUntil(from, until, sub, 1)
} else {
computeAddSub(from, sub, 1)
}
Expand All @@ -108,6 +113,9 @@ func FlagUsagesCustom(flags *pflag.FlagSet, names ...string) string {
if flag.Value.Type() == "string" {
name += " string"
}
if flag.Value.Type() == "int" {
name += " int"
}
tabs := "\t"
if len(name) <= 7 {
tabs = "\t\t"
Expand All @@ -132,7 +140,8 @@ func init() {
rootCmd.PersistentFlags().StringVarP(&from, "from", "F", "", "a base date, time or datetime to use with -A or -S")
rootCmd.PersistentFlags().StringVarP(&add, "add", "A", "", "add: a duration to use with -F, such as '1 day 2 hours 3 seconds'")
rootCmd.PersistentFlags().StringVarP(&sub, "sub", "S", "", "subtract: a duration to use with -F, such as '5 months 4 weeks 3 days'")
rootCmd.PersistentFlags().IntVarP(&recurrence, "recurrence", "R", 0, "recurrence interval")
rootCmd.PersistentFlags().IntVarP(&recurrence, "recurrence", "R", 0, "repeat period this number of times (mutually exclusive with -U)")
rootCmd.PersistentFlags().StringVarP(&until, "until", "U", "", "repeat period until date/time is exceeded")
rootCmd.PersistentFlags().BoolVarP(&noNewline, "nonewline", "n", false, "do not output a newline character")
rootCmd.PersistentFlags().BoolVarP(&readFromStdin, "stdin", "i", false, "read from STDIN instead of using -s/-e")
rootCmd.PersistentFlags().BoolVarP(&brief, "brief", "b", false, "output in brief format, such as: 1Y2M3D4h5m6s7ms8us9ns")
Expand All @@ -145,10 +154,13 @@ func init() {
rootCmd.MarkFlagsMutuallyExclusive("stdin", "add")
rootCmd.MarkFlagsMutuallyExclusive("stdin", "sub")
rootCmd.MarkFlagsMutuallyExclusive("stdin", "recurrence")
rootCmd.MarkFlagsMutuallyExclusive("stdin", "until")
rootCmd.MarkFlagsMutuallyExclusive("from", "start")
rootCmd.MarkFlagsMutuallyExclusive("from", "end")
rootCmd.MarkFlagsMutuallyExclusive("recurrence", "start")
rootCmd.MarkFlagsMutuallyExclusive("recurrence", "end")
rootCmd.MarkFlagsMutuallyExclusive("until", "start")
rootCmd.MarkFlagsMutuallyExclusive("until", "end")
rootCmd.MarkFlagsMutuallyExclusive("add", "start")
rootCmd.MarkFlagsMutuallyExclusive("add", "end")
rootCmd.MarkFlagsMutuallyExclusive("sub", "start")
Expand All @@ -157,6 +169,8 @@ func init() {
rootCmd.MarkFlagsMutuallyExclusive("brief", "add")
rootCmd.MarkFlagsMutuallyExclusive("brief", "sub")
rootCmd.MarkFlagsMutuallyExclusive("brief", "recurrence")
rootCmd.MarkFlagsMutuallyExclusive("brief", "until")
rootCmd.MarkFlagsMutuallyExclusive("until", "recurrence")

versionTemplate := fmt.Sprintf("%s v%s\n%s\n", dtdiff.PgmName, dtdiff.PgmVersion, dtdiff.PgmUrl)
rootCmd.SetVersionTemplate(versionTemplate)
Expand Down Expand Up @@ -265,3 +279,29 @@ func computeAddSubWithRecurrence(from, period string, index, recurrence int) {
}
}
}

func computeUntil(from, until, period string, index int) {
var format []string
var err error
if index == 0 {
format, err = dtdiff.AddUntil(from, until, period)
if err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
} else {
format, err = dtdiff.SubUntil(from, until, period)
if err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
}

if noNewline {
fmt.Print(strings.Join(format, ","))
} else {
for _, f := range format {
fmt.Println(f)
}
}
}
17 changes: 11 additions & 6 deletions cmd/example/expected-output.txt
Original file line number Diff line number Diff line change
Expand Up @@ -6,8 +6,8 @@
2024-03-04T00:00:00Z 2024-06-12T23:59:59Z 2423h59m59s 14W2D23h59m59s
2024-01-01 13:00:00 2024-02-29 13:00:00 1416h0m0s 8 weeks 3 days
2024-01-01 13:00:00 2024-02-29 13:00:00 1416h0m0s 8W3D
2020-01-01 2024-06-28 06:38:44 39365h38m44s 4 years 25 weeks 5 days 5 hours 38 minutes 44 seconds
2020-01-01 2024-06-28 06:38:44 39365h38m44s 4Y25W5D5h38m44s
2020-01-01 2024-06-28 20:45:15 39379h45m15s 4 years 25 weeks 5 days 19 hours 45 minutes 15 seconds
2020-01-01 2024-06-28 20:45:15 39379h45m15s 4Y25W5D19h45m15s

==========================================================================================

Expand All @@ -21,9 +21,6 @@
2024-01-01 00:00:00 1 minute 2 seconds 345 milliseconds 2024-01-01 00:01:02.345 -0500 EST 2023-12-31 23:58:57.655 -0500 EST
2024-01-01 00:00:00 45 seconds 2024-01-01 00:00:45 -0500 EST 2023-12-31 23:59:15 -0500 EST
2024-01-01 00:00:00 1 minute 5 seconds 2024-01-01 00:01:05 -0500 EST 2023-12-31 23:58:55 -0500 EST

==========================================================================================

2024-08-04 05:26:43 +0000 UTC
2024-09-11 06:27:45 +0000 UTC
2024-10-18 07:28:47 +0000 UTC
Expand All @@ -34,4 +31,12 @@
2024-04-14 02:23:37 +0000 UTC
2024-03-07 01:22:35 +0000 UTC
2024-01-31 00:21:33 +0000 UTC
2023-12-24 05:20:31 +0000 UTC
2023-12-24 19:20:31 +0000 UTC

==========================================================================================

2024-08-04 05:26:43 +0000 UTC
2024-09-11 06:27:45 +0000 UTC
2024-10-18 07:28:47 +0000 UTC
2024-11-25 08:29:49 +0000 UTC
2025-01-01 09:30:51 +0000 UTC
20 changes: 18 additions & 2 deletions cmd/example/main.go
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,21 @@ import (
"time"
)

func testUntil() {
from := "2024-06-28T04:25:41Z"
period := "1M1W1h1m2s"
until := "2025-01-01 09:30:51"

all, err := dtdiff.AddUntil(from, until, period)
if err != nil {
fmt.Fprintln(os.Stderr, err)
os.Exit(1)
}
for _, a := range all {
fmt.Println(a)
}
}

func testRecurrence() {
from := "2024-06-28T04:25:41Z"
period := "1M1W1h1m2s"
Expand Down Expand Up @@ -90,10 +105,11 @@ func main() {
fmt.Printf("%s %35s %37s %37s\n", from, period, future, past)
}

testRecurrence()

fmt.Println()
fmt.Println("==========================================================================================")
fmt.Println()

testRecurrence()

testUntil()
}
53 changes: 52 additions & 1 deletion dtdiff.go
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ import (

const (
PgmName string = "dtdiff"
PgmVersion string = "1.2.0"
PgmVersion string = "1.3.0"
PgmUrl string = "https://github.com/jftuga/dtdiff"
)

Expand Down Expand Up @@ -303,3 +303,54 @@ func AddWithRecurrence(from, period string, recurrence int) ([]string, error) {
func SubWithRecurrence(from, period string, recurrence int) ([]string, error) {
return calculateWithRecurrence(from, period, 1, recurrence)
}

// calculateUntil similar to calculate, but returns
// a slice of multiple past or future date/times at intervals until
// the 'until' date/time is exceeded
// index==0 then Add; index==1 then Sub
func calculateUntil(from, until, period string, index int) ([]string, error) {
var all []string
var f, u time.Time
var err error

u, err = now.Parse(until)
if err != nil {
return nil, err
}

for {
from, err = calculate(from, period, index)
if err != nil {
return nil, err
}

f, err = now.Parse(from)
if err != nil {
return nil, err
}

if index == 0 {
if f.After(u) {
break
}
} else {
if f.Before(u) {
break
}
}
all = append(all, from)
}
return all, nil
}

// AddUntil similar to Add, but returns a slice
// of multiple future dates/times until date/time exceed 'until'
func AddUntil(from, until, period string) ([]string, error) {
return calculateUntil(from, until, period, 0)
}

// SubUntil similar to Sub, but returns a slice
// of multiple past dates/times until date/time exceed 'until'
func SubUntil(from, until, period string) ([]string, error) {
return calculateUntil(from, until, period, 1)
}
44 changes: 42 additions & 2 deletions dtdiff_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ func testAddSubWithRecurrence(t *testing.T, from, period string, correctAdd, cor
}
for i := range len(future) {
if !strings.Contains(future[i], correctAdd[i]) {
t.Errorf("[from: %v] [computed: %v] does not contain: [correct: %v]", from, future, correctAdd)
t.Errorf("[from: %v] [computed: %v] does not contain: [correct: %v]", from, future[i], correctAdd[i])
}
}

Expand All @@ -51,7 +51,31 @@ func testAddSubWithRecurrence(t *testing.T, from, period string, correctAdd, cor
}
for j := range len(past) {
if !strings.Contains(past[j], correctSub[j]) {
t.Errorf("[from: %v] [computed: %v] does not contain: [correct: %v]", from, past, correctSub)
t.Errorf("[from: %v] [computed: %v] does not contain: [correct: %v]", from, past[j], correctSub[j])
}
}
}

func testAddUntil(t *testing.T, from, until, period string, correctAdd []string) {
future, err := AddUntil(from, until, period)
if err != nil {
t.Error(err)
}
for i := range len(future) {
if !strings.Contains(future[i], correctAdd[i]) {
t.Errorf("[from: %v] [computed: %v] does not contain: [correct: %v]", from, future[i], correctAdd[i])
}
}
}

func testSubUntil(t *testing.T, from, until, period string, correctSub []string) {
past, err := SubUntil(from, until, period)
if err != nil {
t.Error(err)
}
for i := range len(past) {
if !strings.Contains(past[i], correctSub[i]) {
t.Errorf("[from: %v] [computed: %v] does not contain: [correct: %v]", from, past[i], correctSub[i])
}
}
}
Expand Down Expand Up @@ -205,3 +229,19 @@ func TestWithRecurrence(t *testing.T) {
allCorrectSub := []string{"2024-05-21 03:24:39", "2024-04-14 02:23:37", "2024-03-07 01:22:35"}
testAddSubWithRecurrence(t, from, period, allCorrectAdd, allCorrectSub, recurrence)
}

func TestAddUntil(t *testing.T) {
from := "2024-06-28T04:25:41Z"
period := "1M1W1h1m2s"
until := "2024-10-18 07:28:47"
allCorrectAdd := []string{"2024-08-04 05:26:43", "2024-09-11 06:27:45", "2024-10-18 07:28:47", "x"}
testAddUntil(t, from, until, period, allCorrectAdd)
}

func TestSubUntil(t *testing.T) {
from := "2024-10-18 07:28:47"
period := "1M1W1h1m2s"
until := "2024-05-28T04:25:41Z"
allCorrectSub := []string{"2024-09-11 06:27:45", "2024-08-04 05:26:43", "2024-06-27 04:25:41"}
testSubUntil(t, from, until, period, allCorrectSub)
}