<commit_before>﻿using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Text;

namespace LatencyCollectorCore
{
	static class NtpdInfo
	{
		public static void CheckTimeIsStable()
		{
			try
			{
				var now = DateTime.UtcNow;

				lock (Sync)
				{
					if (now - _lastCheckTime < CheckPeriod)
						return;
				}

				CheckMaxOffset();

				lock (Sync)
				{
					_lastCheckTime = now;
				}
			}
			catch (Exception exc)
			{
				Data.Tracker.Log("Exception", exc);
			}
		}

		private static void CheckMaxOffset()
		{
			var statFiles = new List<string>();
			FindStatFiles(StatsPath1, statFiles);
			FindStatFiles(StatsPath2, statFiles);

			if (statFiles.Count == 0)
				return;

			var now = DateTime.UtcNow;

			statFiles.Sort();

			if ((now - File.GetCreationTimeUtc(statFiles.Last())).TotalDays > 1)
				return;

			statFiles.RemoveAll(file => (now - File.GetCreationTimeUtc(file)).TotalDays > 2);

			if (statFiles.Count > 2)
				statFiles.RemoveRange(0, statFiles.Count - 2);

			var text = new StringBuilder();
			foreach (var file in statFiles)
			{
				using (var stream = new FileStream(file, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
				{
					using (var reader = new StreamReader(stream))
					{
						var curText = reader.ReadToEnd();
						text.Append(curText);
					}
				}
			}

			if (text.Length == 0)
				return;

			CheckMaxOffset(text.ToString());
		}

		static void FindStatFiles(string dirPath, List<string> res)
		{
			if (!Directory.Exists(dirPath))
				return;

			res.AddRange(Directory.GetFiles(dirPath, "loopstats.*", SearchOption.TopDirectoryOnly));
		}

		static void CheckMaxOffset(string text)
		{
			var lines = text.Split(new[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries);
			if (lines.Length == 0)
				throw new ApplicationException();

			var minOffset = double.MaxValue;
			var maxOffset = double.MinValue;

			foreach (var line in lines)
			{
				var offset = GetOffset(line);
				if (offset > maxOffset)
					maxOffset = offset;
				else if (offset < minOffset)
					minOffset = offset;
			}

			var lastOffset = GetOffset(lines.Last());
			Data.Tracker.LogFormat("Info", "ntpd: last {0}, min {1}, max {2}", lastOffset, minOffset, maxOffset);
		}

		private static double GetOffset(string line)
		{
			var columns = line.Split(' ');
			return double.Parse(columns[2]);
		}

		private const string StatsPath1 = @"C:\Program Files\NTP\etc\";
		private const string StatsPath2 = @"C:\Program Files (x86)\NTP\etc\";<commit_msg>correctly parse Modified Julian Date; check only 4 last hours of loopstats<commit_after>﻿using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Text;

namespace LatencyCollectorCore
{
	static class NtpdInfo
	{
		public static void CheckTimeIsStable()
		{
			try
			{
				var now = DateTime.UtcNow;

				lock (Sync)
				{
					if (now - _lastCheckTime < CheckPeriod)
						return;
				}

				CheckMaxOffset();

				lock (Sync)
				{
					_lastCheckTime = now;
				}
			}
			catch (Exception exc)
			{
				Data.Tracker.Log("Exception", exc);
			}
		}

		private static void CheckMaxOffset()
		{
			var statFiles = new List<string>();
			FindStatFiles(StatsPath1, statFiles);
			FindStatFiles(StatsPath2, statFiles);

			if (statFiles.Count == 0)
				return;

			var now = DateTime.UtcNow;

			statFiles.Sort(); // file name reflects date of that file

			statFiles.RemoveAll(file => (now - File.GetCreationTimeUtc(file)).TotalDays > 2);

			if (statFiles.Count > 2)
				statFiles.RemoveRange(0, statFiles.Count - 2);

			var text = new StringBuilder();
			foreach (var file in statFiles)
			{
				using (var stream = new FileStream(file, FileMode.Open, FileAccess.Read, FileShare.ReadWrite))
				{
					using (var reader = new StreamReader(stream))
					{
						var curText = reader.ReadToEnd();
						text.Append(curText);
					}
				}
			}

			if (text.Length == 0)
				return;

			CheckMaxOffset(text.ToString());
		}

		static void FindStatFiles(string dirPath, List<string> res)
		{
			if (!Directory.Exists(dirPath))
				return;

			res.AddRange(Directory.GetFiles(dirPath, "loopstats.*", SearchOption.TopDirectoryOnly));
		}

		static void CheckMaxOffset(string text)
		{
			var lines = ReadLines(text, TimeSpan.FromHours(4));
			if (lines.Count() == 0)
				return;
			if ((DateTime.UtcNow - GetLineTime(lines.Last())).TotalHours > 1)
				return;

			var minOffset = double.MaxValue;
			var maxOffset = double.MinValue;

			foreach (var line in lines)
			{
				var offset = GetOffset(line);
				if (offset > maxOffset)
					maxOffset = offset;
				else if (offset < minOffset)
					minOffset = offset;
			}

			var lastOffset = GetOffset(lines.Last());
			Data.Tracker.LogFormat("Info", "ntpd: last {0}, min {1}, max {2}", lastOffset, minOffset, maxOffset);
		}

		private static string[] ReadLines(string text, TimeSpan period)
		{
			var now = DateTime.UtcNow;
			var lines = text.Split(new[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries);
			var res = new List<string>();

			foreach (var line in lines)
			{
				var lineTime = GetLineTime(line);
				if (now - lineTime < period)
					res.Add(line);
			}

			return res.ToArray();
		}

		private static DateTime GetLineTime(string line)
		{
			// see http://en.wikipedia.org/wiki/Julian_day
			var columns = line.Split(' ');
			var modifiedJulianDate = int.Parse(columns[0]);
			var date = new DateTime(1858, 11, 17).AddDays(modifiedJulianDate);
			var timeNumber = double.Parse(columns[1]);
			var res = date.AddSeconds(timeNumber);
			return res;
		}

		private static double GetOffset(string line)
		{
			var columns = line.Split(' ');
			return double.Parse(columns[2]);
		}

		private const string StatsPath1 = @"C:\Program Files\NTP\etc\";
		private const string StatsPath2 = @"C:\Program Files (x86)\NTP\etc\";
{'fraction_non_alphanumeric': 0.12643678160919541, 'fraction_numerical': 0.008113590263691683, 'mean_word_length': 3.181625441696113, 'pattern_counts': {'":': 0, '<': 13, '<?xml version=': 0, '>': 18, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '7566385', 'n_tokens_mistral': 2401, 'n_tokens_neox': 1892, 'n_words': 471}