117 lines
3.6 KiB
C#
117 lines
3.6 KiB
C#
using PlotLine.Models;
|
|
|
|
namespace PlotLine.Services;
|
|
|
|
public sealed class CharacterAgeDisplayResult
|
|
{
|
|
public bool HasExactAge { get; init; }
|
|
public string? Label { get; init; }
|
|
public string? DisplayText { get; init; }
|
|
}
|
|
|
|
public static class CharacterAgeDisplayService
|
|
{
|
|
public static CharacterAgeDisplayResult GetAgeForScene(SceneCharacter character, string? sceneTimeModeName, DateTime? sceneStartDateTime)
|
|
{
|
|
var effectiveDate = ResolveEffectiveSceneDate(sceneTimeModeName, sceneStartDateTime);
|
|
if (character.BirthDate.HasValue && effectiveDate.HasValue)
|
|
{
|
|
return new CharacterAgeDisplayResult
|
|
{
|
|
HasExactAge = true,
|
|
Label = "Age in scene",
|
|
DisplayText = FormatExactAge(character.BirthDate.Value.Date, effectiveDate.Value.Date)
|
|
};
|
|
}
|
|
|
|
if (character.AgeAtSeriesStart.HasValue)
|
|
{
|
|
return new CharacterAgeDisplayResult
|
|
{
|
|
Label = "Approximate age",
|
|
DisplayText = $"About {character.AgeAtSeriesStart.Value}"
|
|
};
|
|
}
|
|
|
|
return new CharacterAgeDisplayResult();
|
|
}
|
|
|
|
public static CharacterAgeDisplayResult GetAgeForScene(Character character, string? sceneTimeModeName, DateTime? sceneStartDateTime)
|
|
{
|
|
var effectiveDate = ResolveEffectiveSceneDate(sceneTimeModeName, sceneStartDateTime);
|
|
if (character.BirthDate.HasValue && effectiveDate.HasValue)
|
|
{
|
|
return new CharacterAgeDisplayResult
|
|
{
|
|
HasExactAge = true,
|
|
Label = "Age in scene",
|
|
DisplayText = FormatExactAge(character.BirthDate.Value.Date, effectiveDate.Value.Date)
|
|
};
|
|
}
|
|
|
|
if (character.AgeAtSeriesStart.HasValue)
|
|
{
|
|
return new CharacterAgeDisplayResult
|
|
{
|
|
Label = "Approximate age",
|
|
DisplayText = $"About {character.AgeAtSeriesStart.Value}"
|
|
};
|
|
}
|
|
|
|
return new CharacterAgeDisplayResult();
|
|
}
|
|
|
|
public static DateTime? ResolveEffectiveSceneDate(string? timeModeName, DateTime? startDateTime)
|
|
{
|
|
if (!startDateTime.HasValue)
|
|
{
|
|
return null;
|
|
}
|
|
|
|
return string.Equals(timeModeName, "Exact Date", StringComparison.OrdinalIgnoreCase)
|
|
|| string.Equals(timeModeName, "Exact DateTime", StringComparison.OrdinalIgnoreCase)
|
|
|| string.Equals(timeModeName, "Exact Date and Time", StringComparison.OrdinalIgnoreCase)
|
|
? startDateTime.Value.Date
|
|
: null;
|
|
}
|
|
|
|
public static string FormatExactAge(DateTime birthDate, DateTime sceneDate)
|
|
{
|
|
var (years, months) = CalculateExactAge(birthDate, sceneDate);
|
|
|
|
return months == 0
|
|
? $"{years} year{Pluralise(years)}"
|
|
: $"{years} year{Pluralise(years)}, {months} month{Pluralise(months)}";
|
|
}
|
|
|
|
public static (int Years, int Months) CalculateExactAge(DateTime birthDate, DateTime sceneDate)
|
|
{
|
|
var years = sceneDate.Year - birthDate.Year;
|
|
if (sceneDate < birthDate.AddYears(years))
|
|
{
|
|
years--;
|
|
}
|
|
|
|
if (years < 0)
|
|
{
|
|
years = 0;
|
|
}
|
|
|
|
var lastBirthday = birthDate.AddYears(years);
|
|
var months = ((sceneDate.Year - lastBirthday.Year) * 12) + sceneDate.Month - lastBirthday.Month;
|
|
if (sceneDate.Day < lastBirthday.Day)
|
|
{
|
|
months--;
|
|
}
|
|
|
|
if (months < 0)
|
|
{
|
|
months = 0;
|
|
}
|
|
|
|
return (years, months);
|
|
}
|
|
|
|
private static string Pluralise(int value) => value == 1 ? string.Empty : "s";
|
|
}
|