Migrate to date/time format extension methods

This commit is contained in:
Ske 2020-06-21 16:05:04 +02:00
parent dcffbef805
commit d3cf382e3b
10 changed files with 40 additions and 36 deletions

View File

@ -17,7 +17,7 @@ namespace PluralKit.API
o.Add("description", system.DescriptionFor(ctx)); o.Add("description", system.DescriptionFor(ctx));
o.Add("tag", system.Tag); o.Add("tag", system.Tag);
o.Add("avatar_url", system.AvatarUrl); o.Add("avatar_url", system.AvatarUrl);
o.Add("created", DateTimeFormats.TimestampExportFormat.Format(system.Created)); o.Add("created", system.Created.FormatExport());
o.Add("tz", system.UiTz); o.Add("tz", system.UiTz);
o.Add("description_privacy", ctx == LookupContext.ByOwner ? system.DescriptionPrivacy.ToJsonString() : null); o.Add("description_privacy", ctx == LookupContext.ByOwner ? system.DescriptionPrivacy.ToJsonString() : null);
o.Add("member_list_privacy", ctx == LookupContext.ByOwner ? system.MemberListPrivacy.ToJsonString() : null); o.Add("member_list_privacy", ctx == LookupContext.ByOwner ? system.MemberListPrivacy.ToJsonString() : null);
@ -42,8 +42,6 @@ namespace PluralKit.API
public static JObject ToJson(this PKMember member, LookupContext ctx) public static JObject ToJson(this PKMember member, LookupContext ctx)
{ {
var bday = member.BirthdayFor(ctx);
var created = member.CreatedFor(ctx);
var includePrivacy = ctx == LookupContext.ByOwner; var includePrivacy = ctx == LookupContext.ByOwner;
var o = new JObject(); var o = new JObject();
@ -52,7 +50,7 @@ namespace PluralKit.API
// o.Add("color", member.ColorPrivacy.CanAccess(ctx) ? member.Color : null); // o.Add("color", member.ColorPrivacy.CanAccess(ctx) ? member.Color : null);
o.Add("color", member.Color); o.Add("color", member.Color);
o.Add("display_name", member.NamePrivacy.CanAccess(ctx) ? member.DisplayName : null); o.Add("display_name", member.NamePrivacy.CanAccess(ctx) ? member.DisplayName : null);
o.Add("birthday", bday.HasValue ? DateTimeFormats.DateExportFormat.Format(bday.Value) : null); o.Add("birthday", member.BirthdayFor(ctx)?.FormatExport());
o.Add("pronouns", member.PronounsFor(ctx)); o.Add("pronouns", member.PronounsFor(ctx));
o.Add("avatar_url", member.AvatarFor(ctx)); o.Add("avatar_url", member.AvatarFor(ctx));
o.Add("description", member.DescriptionFor(ctx)); o.Add("description", member.DescriptionFor(ctx));
@ -75,7 +73,7 @@ namespace PluralKit.API
// o.Add("color_privacy", ctx == LookupContext.ByOwner ? (member.ColorPrivacy.LevelName()) : null); // o.Add("color_privacy", ctx == LookupContext.ByOwner ? (member.ColorPrivacy.LevelName()) : null);
o.Add("metadata_privacy", includePrivacy ? (member.MetadataPrivacy.LevelName()) : null); o.Add("metadata_privacy", includePrivacy ? (member.MetadataPrivacy.LevelName()) : null);
o.Add("created", created.HasValue ? DateTimeFormats.TimestampExportFormat.Format(created.Value) : null); o.Add("created", member.CreatedFor(ctx)?.FormatExport());
if (member.ProxyTags.Count > 0) if (member.ProxyTags.Count > 0)
{ {

View File

@ -83,7 +83,7 @@ namespace PluralKit.Bot {
embed embed
.AddField("Current shard", $"Shard #{shardId} (of {shardTotal} total, {shardUpTotal} are up)", true) .AddField("Current shard", $"Shard #{shardId} (of {shardTotal} total, {shardUpTotal} are up)", true)
.AddField("Shard uptime", $"{DateTimeFormats.DurationFormat.Format(shardUptime)} ({shardInfo.DisconnectionCount} disconnections)", true) .AddField("Shard uptime", $"{shardUptime.FormatDuration()} ({shardInfo.DisconnectionCount} disconnections)", true)
.AddField("CPU usage", $"{_cpu.LastCpuMeasure:P1}", true) .AddField("CPU usage", $"{_cpu.LastCpuMeasure:P1}", true)
.AddField("Memory usage", $"{memoryUsage / 1024 / 1024} MiB", true) .AddField("Memory usage", $"{memoryUsage / 1024 / 1024} MiB", true)
.AddField("Latency", $"API: {apiLatency.TotalMilliseconds:F0} ms, shard: {shardInfo.ShardLatency.Milliseconds} ms", true) .AddField("Latency", $"API: {apiLatency.TotalMilliseconds:F0} ms, shard: {shardInfo.ShardLatency.Milliseconds} ms", true)

View File

@ -103,10 +103,10 @@ namespace PluralKit.Bot
// But, we do a prompt to confirm. // But, we do a prompt to confirm.
var lastSwitchMembers = _data.GetSwitchMembers(lastTwoSwitches[0]); var lastSwitchMembers = _data.GetSwitchMembers(lastTwoSwitches[0]);
var lastSwitchMemberStr = string.Join(", ", await lastSwitchMembers.Select(m => m.NameFor(ctx)).ToListAsync()); var lastSwitchMemberStr = string.Join(", ", await lastSwitchMembers.Select(m => m.NameFor(ctx)).ToListAsync());
var lastSwitchTimeStr = DateTimeFormats.ZonedDateTimeFormat.Format(lastTwoSwitches[0].Timestamp.InZone(ctx.System.Zone)); var lastSwitchTimeStr = lastTwoSwitches[0].Timestamp.FormatZoned(ctx.System);
var lastSwitchDeltaStr = DateTimeFormats.DurationFormat.Format(SystemClock.Instance.GetCurrentInstant() - lastTwoSwitches[0].Timestamp); var lastSwitchDeltaStr = (SystemClock.Instance.GetCurrentInstant() - lastTwoSwitches[0].Timestamp).FormatDuration();
var newSwitchTimeStr = DateTimeFormats.ZonedDateTimeFormat.Format(time); var newSwitchTimeStr = time.FormatZoned();
var newSwitchDeltaStr = DateTimeFormats.DurationFormat.Format(SystemClock.Instance.GetCurrentInstant() - time.ToInstant()); var newSwitchDeltaStr = (SystemClock.Instance.GetCurrentInstant() - time.ToInstant()).FormatDuration();
// yeet // yeet
var msg = await ctx.Reply($"{Emojis.Warn} This will move the latest switch ({lastSwitchMemberStr}) from {lastSwitchTimeStr} ({lastSwitchDeltaStr} ago) to {newSwitchTimeStr} ({newSwitchDeltaStr} ago). Is this OK?"); var msg = await ctx.Reply($"{Emojis.Warn} This will move the latest switch ({lastSwitchMemberStr}) from {lastSwitchTimeStr} ({lastSwitchDeltaStr} ago) to {newSwitchTimeStr} ({newSwitchDeltaStr} ago). Is this OK?");
@ -138,7 +138,7 @@ namespace PluralKit.Bot
var lastSwitchMembers = _data.GetSwitchMembers(lastTwoSwitches[0]); var lastSwitchMembers = _data.GetSwitchMembers(lastTwoSwitches[0]);
var lastSwitchMemberStr = string.Join(", ", await lastSwitchMembers.Select(m => m.NameFor(ctx)).ToListAsync()); var lastSwitchMemberStr = string.Join(", ", await lastSwitchMembers.Select(m => m.NameFor(ctx)).ToListAsync());
var lastSwitchDeltaStr = DateTimeFormats.DurationFormat.Format(SystemClock.Instance.GetCurrentInstant() - lastTwoSwitches[0].Timestamp); var lastSwitchDeltaStr = (SystemClock.Instance.GetCurrentInstant() - lastTwoSwitches[0].Timestamp).FormatDuration();
DiscordMessage msg; DiscordMessage msg;
if (lastTwoSwitches.Count == 1) if (lastTwoSwitches.Count == 1)
@ -150,7 +150,7 @@ namespace PluralKit.Bot
{ {
var secondSwitchMembers = _data.GetSwitchMembers(lastTwoSwitches[1]); var secondSwitchMembers = _data.GetSwitchMembers(lastTwoSwitches[1]);
var secondSwitchMemberStr = string.Join(", ", await secondSwitchMembers.Select(m => m.NameFor(ctx)).ToListAsync()); var secondSwitchMemberStr = string.Join(", ", await secondSwitchMembers.Select(m => m.NameFor(ctx)).ToListAsync());
var secondSwitchDeltaStr = DateTimeFormats.DurationFormat.Format(SystemClock.Instance.GetCurrentInstant() - lastTwoSwitches[1].Timestamp); var secondSwitchDeltaStr = (SystemClock.Instance.GetCurrentInstant() - lastTwoSwitches[1].Timestamp).FormatDuration();
msg = await ctx.Reply( msg = await ctx.Reply(
$"{Emojis.Warn} This will delete the latest switch ({lastSwitchMemberStr}, {lastSwitchDeltaStr} ago). The next latest switch is {secondSwitchMemberStr} ({secondSwitchDeltaStr} ago). Is this okay?"); $"{Emojis.Warn} This will delete the latest switch ({lastSwitchMemberStr}, {lastSwitchDeltaStr} ago). The next latest switch is {secondSwitchMemberStr} ({secondSwitchDeltaStr} ago). Is this okay?");
} }

View File

@ -226,7 +226,7 @@ namespace PluralKit.Bot
if (zoneStr == null) if (zoneStr == null)
{ {
await ctx.Reply( await ctx.Reply(
$"Your current system time zone is set to **{ctx.System.UiTz}**. It is currently **{DateTimeFormats.ZonedDateTimeFormat.Format(SystemClock.Instance.GetCurrentInstant().InZone(ctx.System.Zone))}** in that time zone. To change your system time zone, type `pk;s tz <zone>`."); $"Your current system time zone is set to **{ctx.System.UiTz}**. It is currently **{SystemClock.Instance.GetCurrentInstant().FormatZoned(ctx.System)}** in that time zone. To change your system time zone, type `pk;s tz <zone>`.");
return; return;
} }
@ -235,7 +235,7 @@ namespace PluralKit.Bot
var currentTime = SystemClock.Instance.GetCurrentInstant().InZone(zone); var currentTime = SystemClock.Instance.GetCurrentInstant().InZone(zone);
var msg = await ctx.Reply( var msg = await ctx.Reply(
$"This will change the system time zone to **{zone.Id}**. The current time is **{DateTimeFormats.ZonedDateTimeFormat.Format(currentTime)}**. Is this correct?"); $"This will change the system time zone to **{zone.Id}**. The current time is **{currentTime.FormatZoned()}**. Is this correct?");
if (!await ctx.PromptYesNo(msg)) throw Errors.TimezoneChangeCancelled; if (!await ctx.PromptYesNo(msg)) throw Errors.TimezoneChangeCancelled;
ctx.System.UiTz = zone.Id; ctx.System.UiTz = zone.Id;
await _data.SaveSystem(ctx.System); await _data.SaveSystem(ctx.System);

View File

@ -79,12 +79,12 @@ namespace PluralKit.Bot
// Calculate the time between the last switch (that we iterated - ie. the next one on the timeline) and the current one // Calculate the time between the last switch (that we iterated - ie. the next one on the timeline) and the current one
var switchDuration = lastSw.Value - sw.Timestamp; var switchDuration = lastSw.Value - sw.Timestamp;
stringToAdd = stringToAdd =
$"**{membersStr}** ({DateTimeFormats.ZonedDateTimeFormat.Format(sw.Timestamp.InZone(system.Zone))}, {DateTimeFormats.DurationFormat.Format(switchSince)} ago, for {DateTimeFormats.DurationFormat.Format(switchDuration)})\n"; $"**{membersStr}** ({sw.Timestamp.FormatZoned(system.Zone)}, {switchSince.FormatDuration()} ago, for {switchDuration.FormatDuration()})\n";
} }
else else
{ {
stringToAdd = stringToAdd =
$"**{membersStr}** ({DateTimeFormats.ZonedDateTimeFormat.Format(sw.Timestamp.InZone(system.Zone))}, {DateTimeFormats.DurationFormat.Format(switchSince)} ago)\n"; $"**{membersStr}** ({sw.Timestamp.FormatZoned(system.Zone)}, {switchSince.FormatDuration()} ago)\n";
} }
try // Unfortunately the only way to test DiscordEmbedBuilder.Description max length is this try // Unfortunately the only way to test DiscordEmbedBuilder.Description max length is this
{ {

View File

@ -82,7 +82,7 @@ namespace PluralKit.Bot {
public static PKError SwitchTimeInFuture => new PKError("Can't move switch to a time in the future."); public static PKError SwitchTimeInFuture => new PKError("Can't move switch to a time in the future.");
public static PKError NoRegisteredSwitches => new PKError("There are no registered switches for this system."); public static PKError NoRegisteredSwitches => new PKError("There are no registered switches for this system.");
public static PKError SwitchMoveBeforeSecondLast(ZonedDateTime time) => new PKError($"Can't move switch to before last switch time ({DateTimeFormats.ZonedDateTimeFormat.Format(time)}), as it would cause conflicts."); public static PKError SwitchMoveBeforeSecondLast(ZonedDateTime time) => new PKError($"Can't move switch to before last switch time ({time.FormatZoned()}), as it would cause conflicts.");
public static PKError SwitchMoveCancelled => new PKError("Switch move cancelled."); public static PKError SwitchMoveCancelled => new PKError("Switch move cancelled.");
public static PKError SwitchDeleteCancelled => new PKError("Switch deletion cancelled."); public static PKError SwitchDeleteCancelled => new PKError("Switch deletion cancelled.");
public static PKError TimezoneParseError(string timezone) => new PKError($"Could not parse timezone offset {timezone}. Offset must be a value like 'UTC+5' or 'GMT-4:30'."); public static PKError TimezoneParseError(string timezone) => new PKError($"Could not parse timezone offset {timezone}. Offset must be a value like 'UTC+5' or 'GMT-4:30'.");

View File

@ -22,8 +22,6 @@ namespace PluralKit.Bot
public void RenderPage(DiscordEmbedBuilder eb, DateTimeZone zone, IEnumerable<ListedMember> members, LookupContext ctx) public void RenderPage(DiscordEmbedBuilder eb, DateTimeZone zone, IEnumerable<ListedMember> members, LookupContext ctx)
{ {
string FormatTimestamp(Instant timestamp) => DateTimeFormats.ZonedDateTimeFormat.Format(timestamp.InZone(zone));
foreach (var m in members) foreach (var m in members)
{ {
var profile = $"**ID**: {m.Hid}"; var profile = $"**ID**: {m.Hid}";
@ -32,8 +30,8 @@ namespace PluralKit.Bot
if (_fields.ShowBirthday && m.BirthdayFor(ctx) != null) profile += $"\n**Birthdate**: {m.BirthdayString}"; if (_fields.ShowBirthday && m.BirthdayFor(ctx) != null) profile += $"\n**Birthdate**: {m.BirthdayString}";
if (_fields.ShowProxyTags && m.ProxyTags.Count > 0) profile += $"\n**Proxy tags:** {m.ProxyTagsString()}"; if (_fields.ShowProxyTags && m.ProxyTags.Count > 0) profile += $"\n**Proxy tags:** {m.ProxyTagsString()}";
if (_fields.ShowMessageCount && m.MessageCountFor(ctx) is {} count && count > 0) profile += $"\n**Message count:** {count}"; if (_fields.ShowMessageCount && m.MessageCountFor(ctx) is {} count && count > 0) profile += $"\n**Message count:** {count}";
if (_fields.ShowLastMessage && m.MetadataPrivacy.TryGet(ctx, m.LastMessage, out var lastMsg)) profile += $"\n**Last message:** {FormatTimestamp(DiscordUtils.SnowflakeToInstant(lastMsg.Value))}"; if (_fields.ShowLastMessage && m.MetadataPrivacy.TryGet(ctx, m.LastMessage, out var lastMsg)) profile += $"\n**Last message:** {DiscordUtils.SnowflakeToInstant(lastMsg.Value).FormatZoned(zone)}";
if (_fields.ShowLastSwitch && m.MetadataPrivacy.TryGet(ctx, m.LastSwitchTime, out var lastSw)) profile += $"\n**Last switched in:** {FormatTimestamp(lastSw.Value)}"; if (_fields.ShowLastSwitch && m.MetadataPrivacy.TryGet(ctx, m.LastSwitchTime, out var lastSw)) profile += $"\n**Last switched in:** {lastSw.Value.FormatZoned(zone)}";
if (_fields.ShowDescription && m.DescriptionFor(ctx) is {} desc) profile += $"\n\n{desc}"; if (_fields.ShowDescription && m.DescriptionFor(ctx) is {} desc) profile += $"\n\n{desc}";
if (_fields.ShowPrivacy && m.MemberVisibility == PrivacyLevel.Private) profile += "\n*(this member is hidden)*"; if (_fields.ShowPrivacy && m.MemberVisibility == PrivacyLevel.Private) profile += "\n*(this member is hidden)*";

View File

@ -39,7 +39,7 @@ namespace PluralKit.Bot {
.WithColor(DiscordUtils.Gray) .WithColor(DiscordUtils.Gray)
.WithTitle(system.Name ?? null) .WithTitle(system.Name ?? null)
.WithThumbnail(system.AvatarUrl) .WithThumbnail(system.AvatarUrl)
.WithFooter($"System ID: {system.Hid} | Created on {DateTimeFormats.ZonedDateTimeFormat.Format(system.Created.InZone(system.Zone))}"); .WithFooter($"System ID: {system.Hid} | Created on {system.Created.FormatZoned(system)}");
var latestSwitch = await _data.GetLatestSwitch(system.Id); var latestSwitch = await _data.GetLatestSwitch(system.Id);
if (latestSwitch != null && system.FrontPrivacy.CanAccess(ctx)) if (latestSwitch != null && system.FrontPrivacy.CanAccess(ctx))
@ -112,7 +112,7 @@ namespace PluralKit.Bot {
.WithAuthor(name, iconUrl: DiscordUtils.WorkaroundForUrlBug(avatar)) .WithAuthor(name, iconUrl: DiscordUtils.WorkaroundForUrlBug(avatar))
// .WithColor(member.ColorPrivacy.CanAccess(ctx) ? color : DiscordUtils.Gray) // .WithColor(member.ColorPrivacy.CanAccess(ctx) ? color : DiscordUtils.Gray)
.WithColor(color) .WithColor(color)
.WithFooter($"System ID: {system.Hid} | Member ID: {member.Hid} {(member.MetadataPrivacy.CanAccess(ctx) ? $"| Created on {DateTimeFormats.ZonedDateTimeFormat.Format(member.Created.InZone(system.Zone))}":"")}"); .WithFooter($"System ID: {system.Hid} | Member ID: {member.Hid} {(member.MetadataPrivacy.CanAccess(ctx) ? $"| Created on {member.Created.FormatZoned(system)}":"")}");
var description = ""; var description = "";
if (member.MemberVisibility == PrivacyLevel.Private) description += "*(this member is hidden)*\n"; if (member.MemberVisibility == PrivacyLevel.Private) description += "*(this member is hidden)*\n";
@ -149,7 +149,7 @@ namespace PluralKit.Bot {
return new DiscordEmbedBuilder() return new DiscordEmbedBuilder()
.WithColor(members.FirstOrDefault()?.Color?.ToDiscordColor() ?? DiscordUtils.Gray) .WithColor(members.FirstOrDefault()?.Color?.ToDiscordColor() ?? DiscordUtils.Gray)
.AddField($"Current {"fronter".ToQuantity(members.Count, ShowQuantityAs.None)}", members.Count > 0 ? string.Join(", ", members.Select(m => m.NameFor(ctx))) : "*(no fronter)*") .AddField($"Current {"fronter".ToQuantity(members.Count, ShowQuantityAs.None)}", members.Count > 0 ? string.Join(", ", members.Select(m => m.NameFor(ctx))) : "*(no fronter)*")
.AddField("Since", $"{DateTimeFormats.ZonedDateTimeFormat.Format(sw.Timestamp.InZone(zone))} ({DateTimeFormats.DurationFormat.Format(timeSinceSwitch)} ago)") .AddField("Since", $"{sw.Timestamp.FormatZoned(zone)} ({timeSinceSwitch.FormatDuration()} ago)")
.Build(); .Build();
} }
@ -200,7 +200,7 @@ namespace PluralKit.Bot {
var actualPeriod = breakdown.RangeEnd - breakdown.RangeStart; var actualPeriod = breakdown.RangeEnd - breakdown.RangeStart;
var eb = new DiscordEmbedBuilder() var eb = new DiscordEmbedBuilder()
.WithColor(DiscordUtils.Gray) .WithColor(DiscordUtils.Gray)
.WithFooter($"Since {DateTimeFormats.ZonedDateTimeFormat.Format(breakdown.RangeStart.InZone(tz))} ({DateTimeFormats.DurationFormat.Format(actualPeriod)} ago)"); .WithFooter($"Since {breakdown.RangeStart.FormatZoned(tz)} ({actualPeriod.FormatDuration()} ago)");
var maxEntriesToDisplay = 24; // max 25 fields allowed in embed - reserve 1 for "others" var maxEntriesToDisplay = 24; // max 25 fields allowed in embed - reserve 1 for "others"
@ -214,14 +214,15 @@ namespace PluralKit.Bot {
foreach (var pair in membersOrdered) foreach (var pair in membersOrdered)
{ {
var frac = pair.Value / actualPeriod; var frac = pair.Value / actualPeriod;
eb.AddField(pair.Key?.NameFor(ctx) ?? "*(no fronter)*", $"{frac*100:F0}% ({DateTimeFormats.DurationFormat.Format(pair.Value)})"); eb.AddField(pair.Key?.NameFor(ctx) ?? "*(no fronter)*", $"{frac*100:F0}% ({pair.Value.FormatDuration()})");
} }
if (membersOrdered.Count > maxEntriesToDisplay) if (membersOrdered.Count > maxEntriesToDisplay)
{ {
eb.AddField("(others)", eb.AddField("(others)",
DateTimeFormats.DurationFormat.Format(membersOrdered.Skip(maxEntriesToDisplay) membersOrdered.Skip(maxEntriesToDisplay)
.Aggregate(Duration.Zero, (prod, next) => prod + next.Value)), true); .Aggregate(Duration.Zero, (prod, next) => prod + next.Value)
.FormatDuration(), true);
} }
return Task.FromResult(eb.Build()); return Task.FromResult(eb.Build());

View File

@ -37,13 +37,13 @@ namespace PluralKit.Core
Name = m.Name, Name = m.Name,
DisplayName = m.DisplayName, DisplayName = m.DisplayName,
Description = m.Description, Description = m.Description,
Birthday = m.Birthday != null ? DateTimeFormats.DateExportFormat.Format(m.Birthday.Value) : null, Birthday = m.Birthday?.FormatExport(),
Pronouns = m.Pronouns, Pronouns = m.Pronouns,
Color = m.Color, Color = m.Color,
AvatarUrl = m.AvatarUrl, AvatarUrl = m.AvatarUrl,
ProxyTags = m.ProxyTags, ProxyTags = m.ProxyTags,
KeepProxy = m.KeepProxy, KeepProxy = m.KeepProxy,
Created = DateTimeFormats.TimestampExportFormat.Format(m.Created), Created = m.Created.FormatExport(),
MessageCount = m.MessageCount MessageCount = m.MessageCount
})) members.Add(member); })) members.Add(member);
@ -52,7 +52,7 @@ namespace PluralKit.Core
var switchList = await _data.GetPeriodFronters(system, Instant.FromDateTimeUtc(DateTime.MinValue.ToUniversalTime()), SystemClock.Instance.GetCurrentInstant()); var switchList = await _data.GetPeriodFronters(system, Instant.FromDateTimeUtc(DateTime.MinValue.ToUniversalTime()), SystemClock.Instance.GetCurrentInstant());
switches.AddRange(switchList.Select(x => new DataFileSwitch switches.AddRange(switchList.Select(x => new DataFileSwitch
{ {
Timestamp = DateTimeFormats.TimestampExportFormat.Format(x.TimespanStart), Timestamp = x.TimespanStart.FormatExport(),
Members = x.Members.Select(m => m.Hid).ToList() // Look up member's HID using the member export from above Members = x.Members.Select(m => m.Hid).ToList() // Look up member's HID using the member export from above
})); }));
@ -67,7 +67,7 @@ namespace PluralKit.Core
TimeZone = system.UiTz, TimeZone = system.UiTz,
Members = members, Members = members,
Switches = switches, Switches = switches,
Created = DateTimeFormats.TimestampExportFormat.Format(system.Created), Created = system.Created.FormatExport(),
LinkedAccounts = (await _data.GetSystemAccounts(system)).ToList() LinkedAccounts = (await _data.GetSystemAccounts(system)).ToList()
}; };
} }
@ -333,16 +333,16 @@ namespace PluralKit.Core
tags.Add(new ProxyTag(Brackets[i * 2], Brackets[i * 2 + 1])); tags.Add(new ProxyTag(Brackets[i * 2], Brackets[i * 2 + 1]));
// Convert birthday from ISO timestamp format to ISO date // Convert birthday from ISO timestamp format to ISO date
var convertedBirthdate = Birthday != null ? DateTimeFormats.DateExportFormat.Format( var convertedBirthdate = Birthday != null
LocalDate.FromDateTime(DateTimeFormats.TimestampExportFormat.Parse(Birthday).Value ? LocalDate.FromDateTime(DateTimeFormats.TimestampExportFormat.Parse(Birthday).Value.ToDateTimeUtc())
.ToDateTimeUtc())) : null; : (LocalDate?) null;
return new DataFileMember return new DataFileMember
{ {
Id = Guid.NewGuid().ToString(), // Note: this is only ever used for lookup purposes Id = Guid.NewGuid().ToString(), // Note: this is only ever used for lookup purposes
Name = Name, Name = Name,
AvatarUrl = AvatarUrl, AvatarUrl = AvatarUrl,
Birthday = convertedBirthdate, Birthday = convertedBirthdate?.FormatExport(),
Description = Description, Description = Description,
ProxyTags = tags, ProxyTags = tags,
KeepProxy = ShowBrackets, KeepProxy = ShowBrackets,

View File

@ -20,5 +20,12 @@ namespace PluralKit.Core {
public static IPattern<LocalDateTime> LocalDateTimeFormat = LocalDateTimePattern.CreateWithInvariantCulture("yyyy-MM-dd HH:mm:ss"); public static IPattern<LocalDateTime> LocalDateTimeFormat = LocalDateTimePattern.CreateWithInvariantCulture("yyyy-MM-dd HH:mm:ss");
public static IPattern<ZonedDateTime> ZonedDateTimeFormat = ZonedDateTimePattern.CreateWithInvariantCulture("yyyy-MM-dd HH:mm:ss x", DateTimeZoneProviders.Tzdb); public static IPattern<ZonedDateTime> ZonedDateTimeFormat = ZonedDateTimePattern.CreateWithInvariantCulture("yyyy-MM-dd HH:mm:ss x", DateTimeZoneProviders.Tzdb);
public static string FormatExport(this Instant instant) => TimestampExportFormat.Format(instant);
public static string FormatExport(this LocalDate date) => DateExportFormat.Format(date);
public static string FormatZoned(this ZonedDateTime zdt) => ZonedDateTimeFormat.Format(zdt);
public static string FormatZoned(this Instant i, DateTimeZone zone) => i.InZone(zone).FormatZoned();
public static string FormatZoned(this Instant i, PKSystem sys) => i.FormatZoned(sys.Zone);
public static string FormatDuration(this Duration d) => DurationFormat.Format(d);
} }
} }