Skip to content

Conversation

@bpeake-illuscio
Copy link
Contributor

@bpeake-illuscio bpeake-illuscio commented Mar 17, 2021

Opening this as a draft, but if you like the API, I think it's ready to be a full PR.

This PR adds several helper methods to the Date, Time, and Datetime types. The work I do involves manipulating DICOM dates and times pretty frequently, so I thought it might be nice to add some quality-of-life features to follow up on the groundwork of #171

Since not all values on a given [Type].Time field are valid due to low precision of the source values, I've added methods for the relevant time values that also report their presence.

Each type has also received a general method to easily check whether a value has at least some precision value.

An example using Datetime:

// This is a DT value like we would expect
dtString := "2020121012"

dt, err := ParseDatetime(dtString)
if err != nil {
	panic(err)
}

// Our Datetime value has some methods similar to time.Time's methods, but also
// returns presence information since not all DICOM datetimes contain all datetime
// components.
//
// Try to get the Day value. Our value included a day, so 'ok' will be true
if day, ok := dt.Day(); ok {
	fmt.Println("DAY VALUE   :", day)
}

// Try to get the Minute value. Because minutes are not included, 'ok' will be false
// and this will not print.
if minute, ok := dt.Minute(); ok {
	fmt.Println("MINUTE VALUE :", minute)
}

// We can also easily check if the value contains a certain precision:
hasMinutes := dt.HasPrecision(PrecisionMinutes)
fmt.Println("HAS MINUTES :", hasMinutes)

// Output:
// TIME VALUE  : 2020-12-10 12:00:00 +0000 +0000
// PRECISION   : HOURS
// NO OFFSET   : true
// DAY VALUE   : 10
// HAS MINUTES : false

A method has also been added to both the Date and Time types to combine a Date value and a Time value into a single Datetime value:

daString := "20200316"
tmString := "105434.123456"

daParsed, err := ParseDate(daString)
if err != nil {
	panic(err)
}

tmParsed, err := ParseTime(tmString)
if err != nil {
	panic(err)
}

datetime, err := daParsed.Combine(tmParsed, time.UTC)
if err != nil {
	panic(err)
}

fmt.Println("DCM    :", datetime.DCM())
fmt.Println("STRING :", datetime.String())

// Output:
// DCM    : 20200316105434.123456+0000
// STRING : 2020-03-16 10:54:34.123456 +00:00

Both #186 and #188 have been merged into this branch already.

@bpeake-illuscio
Copy link
Contributor Author

bpeake-illuscio commented Mar 26, 2021

I've just pushed an update to this followup that tweaks and adds a couple methods so that you can form a few common interfaces between the dcmtime types. For instance, you can now write a helper function like this:

// DCMTime is a common interface for dcmtime.Date, dcmtime.Time, and dcmtime.Datetime.
type DCMTime interface {
	GetTime() time.Time
	GetPrecision() dcmtime.PrecisionLevel
	DCM() string
}

func InspectDICOMTimeVal(value DCMTime) error {
	// Do something with this value
	return nil
}

The main addition here is adding a GetTime() and GetPrecision() method to each of the dcmtime types. I've been writing some code where a single code path could be used with an interface like this, so I decided it was worth adding in. Callers could do this though wrappers on their own, but having it right out of the box is kind of nice, IMO.

I realize that I am kind of bloating this PR, so let me know if you would like me to try and break this up into smaller PRs.
Thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants