Skip to content
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
7 changes: 7 additions & 0 deletions src/Hl7.Fhir.Base/CompatibilitySuppressions.xml
Original file line number Diff line number Diff line change
Expand Up @@ -344,6 +344,13 @@
<Right>lib/net8.0/Hl7.Fhir.Base.dll</Right>
<IsBaselineSuppression>true</IsBaselineSuppression>
</Suppression>
<Suppression>
<DiagnosticId>CP0002</DiagnosticId>
<Target>M:Hl7.Fhir.Model.Date.ToDateTimeOffset</Target>
<Left>lib/net8.0/Hl7.Fhir.Base.dll</Left>
<Right>lib/net8.0/Hl7.Fhir.Base.dll</Right>
<IsBaselineSuppression>true</IsBaselineSuppression>
</Suppression>
<Suppression>
<DiagnosticId>CP0002</DiagnosticId>
<Target>M:Hl7.Fhir.Model.Meta.get_ProfileElement</Target>
Expand Down
184 changes: 88 additions & 96 deletions src/Hl7.Fhir.Base/Model/Date.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,129 +27,121 @@ POSSIBILITY OF SUCH DAMAGE.


*/
#nullable enable

using System;
using System.Diagnostics.CodeAnalysis;
using P = Hl7.Fhir.ElementModel.Types;

#nullable enable
namespace Hl7.Fhir.Model;

namespace Hl7.Fhir.Model
public partial class Date
{
public partial class Date
public Date(int year, int month, int day)
: this(string.Format(System.Globalization.CultureInfo.InvariantCulture, FhirDateTime.FMT_YEARMONTHDAY, year, month, day))
{
public Date(int year, int month, int day)
: this(string.Format(System.Globalization.CultureInfo.InvariantCulture, FhirDateTime.FMT_YEARMONTHDAY, year, month, day))
{
}
}

public Date(int year, int month)
: this(string.Format(System.Globalization.CultureInfo.InvariantCulture, FhirDateTime.FMT_YEARMONTH, year, month))
{
}
public Date(int year, int month)
: this(string.Format(System.Globalization.CultureInfo.InvariantCulture, FhirDateTime.FMT_YEARMONTH, year, month))
{
}

public Date(int year) : this(string.Format(System.Globalization.CultureInfo.InvariantCulture, FhirDateTime.FMT_YEAR, year))
{
}
public Date(int year) : this(string.Format(System.Globalization.CultureInfo.InvariantCulture, FhirDateTime.FMT_YEAR, year))
{
}

public static Date FromDateTimeOffset(DateTimeOffset date) => new(date.Year, date.Month, date.Day);
public static Date FromDateTimeOffset(DateTimeOffset date) => new(date.Year, date.Month, date.Day);

/// <summary>
/// Gets the current date in the local timezone.
/// </summary>
public static Date Today() => FromDateTimeOffset(DateTimeOffset.Now);
/// <summary>
/// Gets the current date in the local timezone.
/// </summary>
public static Date Today() => FromDateTimeOffset(DateTimeOffset.Now);

/// <summary>
/// Gets the current date in UTC.
/// </summary>
public static Date UtcToday() => FromDateTimeOffset(DateTimeOffset.UtcNow);
/// <summary>
/// Gets the current date in UTC.
/// </summary>
public static Date UtcToday() => FromDateTimeOffset(DateTimeOffset.UtcNow);

[NonSerialized] // To prevent binary serialization from serializing this field
private P.Date? _parsedValue = null;
[NonSerialized] // To prevent binary serialization from serializing this field
private P.Date? _parsedValue = null;

// This is a sentinel value that marks that the current string representation is
// not parseable, so we don't have to try again. It's value is never used, it's just
// checked by reference.
private static readonly P.Date INVALID_VALUE = P.Date.FromDateTimeOffset(DateTimeOffset.MinValue);
// This is a sentinel value that marks that the current string representation is
// not parseable, so we don't have to try again. It's value is never used, it's just
// checked by reference.
private static readonly P.Date INVALID_VALUE = P.Date.FromDateTimeOffset(DateTimeOffset.MinValue);

/// <summary>
/// Converts a Fhir Date to a <see cref="P.Date"/>.
/// </summary>
/// <returns>true if the Fhir Date contains a valid date string, false otherwise.</returns>
public bool TryToDate([NotNullWhen(true)] out P.Date? date)
/// <summary>
/// Converts a Fhir Date to a <see cref="P.Date"/>.
/// </summary>
/// <returns>true if the Fhir Date contains a valid date string, false otherwise.</returns>
public bool TryToDate([NotNullWhen(true)] out P.Date? date)
{
if (_parsedValue is null)
{
if (_parsedValue is null)
{
if (Value is not null && !(P.Date.TryParse(Value, out _parsedValue) && !_parsedValue!.HasOffset))
_parsedValue = INVALID_VALUE;
}

if (hasInvalidParsedValue())
{
date = null;
return false;
}
else
{
date = _parsedValue!;
return true;
}

bool hasInvalidParsedValue() => ReferenceEquals(_parsedValue, INVALID_VALUE);
if (Value is not null && !(P.Date.TryParse(Value, out _parsedValue) && !_parsedValue!.HasOffset))
_parsedValue = INVALID_VALUE;
}

/// <summary>
/// Converts a Fhir Date to a <see cref="P.Date"/>.
/// </summary>
/// <returns>The Date, or null if the <see cref="Value"/> is null.</returns>
/// <exception cref="FormatException">Thrown when the Value does not contain a valid FHIR Date.</exception>
public P.Date? ToDate() => TryToDate(out var dt) ? dt : throw new FormatException($"String '{Value}' was not recognized as a valid date.");

protected override void OnObjectValueChanged()
if (hasInvalidParsedValue())
{
_parsedValue = null;
base.OnObjectValueChanged();
date = null;
return false;
}

/// <summary>
/// Converts this Fhir Fhir Date to a <see cref="DateTimeOffset"/>.
/// </summary>
/// <returns>A DateTimeOffset filled out to midnight, january 1 (UTC) in case of a partial date.</returns>
public DateTimeOffset? ToDateTimeOffset()
{
if (Value == null) return null; // Note: this behaviour is inconsistent with ToDateTimeOffset() in FhirDateTime
date = _parsedValue!;
return true;

// ToDateTimeOffset() will convert partial date/times by filling out to midnight/january 1 UTC
if (!TryToDate(out var dt))
throw new FormatException($"Date '{Value}' was not recognized as a valid datetime.");
bool hasInvalidParsedValue() => ReferenceEquals(_parsedValue, INVALID_VALUE);
}

// Since Value is not null and the parsed value is valid, dto will not be null
return dt!.ToDateTimeOffset(TimeSpan.Zero);
}
/// <summary>
/// Converts a Fhir Date to a <see cref="P.Date"/>.
/// </summary>
/// <returns>The Date, or null if the <see cref="Value"/> is null.</returns>
/// <exception cref="FormatException">Thrown when the Value does not contain a valid FHIR Date.</exception>
public P.Date ToDate() => TryToDate(out var dt) ? dt : throw new FormatException($"String '{Value}' was not recognized as a valid date.");

protected override void OnObjectValueChanged()
{
_parsedValue = null;
base.OnObjectValueChanged();
}

/// <summary>
/// Convert this Fhir Date to a <see cref="DateTimeOffset"/>.
/// </summary>
/// <returns>True if the value of the Fhir Date is not null and can be parsed as a DateTimeOffset, false otherwise.</returns>
public bool TryToDateTimeOffset(out DateTimeOffset dto)
/// <summary>
/// Converts this Fhir Fhir Date to a <see cref="DateTimeOffset"/>.
/// </summary>
/// <returns>A DateTimeOffset filled out to midnight, january 1 (UTC) in case of a partial date.</returns>
public DateTimeOffset ToDateTimeOffset()
{
if (Value == null) throw new InvalidOperationException("Date's value is null.");

// TryToDate() will convert partial date/times by filling out to midnight/january 1 UTC
if (!TryToDate(out var dt))
throw new FormatException($"Date '{Value}' was not recognized as a valid datetime.");

// Since Value is not null and the parsed value is valid, dto will not be null
return dt.ToDateTimeOffset(TimeSpan.Zero);
}

/// <summary>
/// Convert this Fhir Date to a <see cref="DateTimeOffset"/>.
/// </summary>
/// <returns>True if the value of the Fhir Date is not null and can be parsed as a DateTimeOffset, false otherwise.</returns>
public bool TryToDateTimeOffset(out DateTimeOffset dto)
{
if (Value is not null && TryToDate(out var dt))
{
if (Value is not null && TryToDate(out var dt))
{
dto = dt.ToDateTimeOffset(TimeSpan.Zero);
return true;
}
else
{
dto = default;
return false;
}
dto = dt.ToDateTimeOffset(TimeSpan.Zero);
return true;
}

/// <summary>
/// Checks whether the given literal is correctly formatted.
/// </summary>
public static bool IsValidValue(string value) => P.Date.TryParse(value, out var parsed) && !parsed.HasOffset;
dto = default;
return false;
}
}

#nullable restore
/// <summary>
/// Checks whether the given literal is correctly formatted.
/// </summary>
public static bool IsValidValue(string value) => P.Date.TryParse(value, out var parsed) && !parsed.HasOffset;
}
Loading
Loading