using System.Globalization; namespace Nonemm.Contests.Udc; /// `MultipleSessions = 0000/30`: the contest runs in sessions, the first one /// starting at 00:00 UTC and each lasting 30 minutes. A station worked in an /// earlier session may be worked again, and `ResetMultsEverySession` and /// `ResetSNEverySession` say whether the multipliers and the serial numbers /// start over as well. /// /// N1MM writes the length as minutes up to two digits and as hours and minutes /// above that: `30` is half an hour, `200` is two hours. The shortest session /// N1MM takes is ten minutes. public sealed class UdcSessions { private static readonly TimeSpan Shortest = TimeSpan.FromMinutes(10); private readonly TimeSpan start; private readonly TimeSpan length; public UdcSessions(UdcFile file) { ResetsMultipliers = file.Flag("ResetMultsEverySession", false); ResetsSerialNumbers = file.Flag("ResetSNEverySession", false); string[] parts = file.Text("MultipleSessions").Split('/', StringSplitOptions.TrimEntries); if (parts.Length != 2) { return; } TimeSpan? first = HoursAndMinutes(parts[0]); TimeSpan? each = HoursAndMinutes(parts[1]); if (first is null || each is null || each < Shortest) { return; } start = first.Value; length = each.Value; IsDefined = true; } public bool IsDefined { get; } public bool ResetsMultipliers { get; } public bool ResetsSerialNumbers { get; } /// Which session a contact belongs to. The sessions are counted off a /// fixed date so that two days of a contest never share a number, which is /// what N1MM's own stepping from the start time gives. Every contact is in /// session 0 for a contest without sessions. public int SessionOf(DateTime utc) => IsDefined ? (int)((utc - (DateTime.UnixEpoch + start)).Ticks / length.Ticks) : 0; /// `30` is thirty minutes, `200` two hours, `0000` midnight. A value that /// is not a number at all leaves the setting off. private static TimeSpan? HoursAndMinutes(string value) { if (!int.TryParse(value, NumberStyles.None, CultureInfo.InvariantCulture, out int number)) { return null; } return value.Length > 2 ? TimeSpan.FromMinutes((number / 100 * 60) + (number % 100)) : TimeSpan.FromMinutes(number); } }