2017-10-29 10:19:09 +00:00
|
|
|
//********************************************************************************************
|
|
|
|
//Author: Sergey Stoyan, CliverSoft.com
|
|
|
|
// http://cliversoft.com
|
|
|
|
// stoyan@cliversoft.com
|
|
|
|
// sergey.stoyan@gmail.com
|
|
|
|
// 27 February 2007
|
|
|
|
//********************************************************************************************
|
2018-03-10 08:05:56 +00:00
|
|
|
|
2017-10-29 10:19:09 +00:00
|
|
|
using System;
|
2020-11-04 21:23:34 +00:00
|
|
|
using System.Diagnostics.CodeAnalysis;
|
2017-10-29 10:19:09 +00:00
|
|
|
using System.Text.RegularExpressions;
|
2018-03-10 08:05:56 +00:00
|
|
|
|
2020-03-14 16:05:10 +00:00
|
|
|
// ReSharper disable NotAccessedField.Global
|
|
|
|
// ReSharper disable MemberCanBePrivate.Global
|
|
|
|
// ReSharper disable UnusedMember.Global
|
|
|
|
|
2018-03-10 08:05:56 +00:00
|
|
|
namespace DateTimeRoutines
|
2017-10-29 10:19:09 +00:00
|
|
|
{
|
|
|
|
/// <summary>
|
|
|
|
/// Miscellaneous and parsing methods for DateTime
|
|
|
|
/// </summary>
|
2020-11-04 21:23:34 +00:00
|
|
|
[ExcludeFromCodeCoverage] // this library is not changed by Jackett team
|
2017-10-29 10:19:09 +00:00
|
|
|
public static class DateTimeRoutines
|
|
|
|
{
|
|
|
|
#region miscellaneous methods
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// Amount of seconds elapsed between 1970-01-01 00:00:00 and the date-time.
|
|
|
|
/// </summary>
|
2020-03-14 16:05:10 +00:00
|
|
|
/// <param name="dateTime">date-time</param>
|
2017-10-29 10:19:09 +00:00
|
|
|
/// <returns>seconds</returns>
|
2020-03-14 16:05:10 +00:00
|
|
|
public static uint GetSecondsSinceUnixEpoch(this DateTime dateTime)
|
2017-10-29 10:19:09 +00:00
|
|
|
{
|
2020-03-14 16:05:10 +00:00
|
|
|
var t = dateTime - new DateTime(1970, 1, 1);
|
2020-02-10 22:16:19 +00:00
|
|
|
var ss = (int)t.TotalSeconds;
|
2017-10-29 10:19:09 +00:00
|
|
|
if (ss < 0)
|
|
|
|
return 0;
|
|
|
|
return (uint)ss;
|
|
|
|
}
|
|
|
|
|
|
|
|
#endregion
|
|
|
|
|
|
|
|
#region parsing definitions
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// Defines a substring where date-time was found and result of conversion
|
|
|
|
/// </summary>
|
|
|
|
public class ParsedDateTime
|
|
|
|
{
|
|
|
|
/// <summary>
|
|
|
|
/// Index of first char of a date substring found in the string
|
|
|
|
/// </summary>
|
2020-03-14 16:05:10 +00:00
|
|
|
public readonly int IndexOfDate;
|
2017-10-29 10:19:09 +00:00
|
|
|
/// <summary>
|
|
|
|
/// Length a date substring found in the string
|
|
|
|
/// </summary>
|
2020-03-14 16:05:10 +00:00
|
|
|
public readonly int LengthOfDate;
|
2017-10-29 10:19:09 +00:00
|
|
|
/// <summary>
|
|
|
|
/// Index of first char of a time substring found in the string
|
|
|
|
/// </summary>
|
2020-03-14 16:05:10 +00:00
|
|
|
public readonly int IndexOfTime;
|
2017-10-29 10:19:09 +00:00
|
|
|
/// <summary>
|
|
|
|
/// Length of a time substring found in the string
|
|
|
|
/// </summary>
|
2020-03-14 16:05:10 +00:00
|
|
|
public readonly int LengthOfTime;
|
2017-10-29 10:19:09 +00:00
|
|
|
/// <summary>
|
|
|
|
/// DateTime found in the string
|
|
|
|
/// </summary>
|
2020-02-10 22:16:19 +00:00
|
|
|
public readonly DateTime DateTime;
|
2017-10-29 10:19:09 +00:00
|
|
|
/// <summary>
|
|
|
|
/// True if a date was found within the string
|
|
|
|
/// </summary>
|
2020-02-10 22:16:19 +00:00
|
|
|
public readonly bool IsDateFound;
|
2017-10-29 10:19:09 +00:00
|
|
|
/// <summary>
|
|
|
|
/// True if a time was found within the string
|
|
|
|
/// </summary>
|
2020-02-10 22:16:19 +00:00
|
|
|
public readonly bool IsTimeFound;
|
2017-10-29 10:19:09 +00:00
|
|
|
/// <summary>
|
|
|
|
/// UTC offset if it was found within the string
|
|
|
|
/// </summary>
|
2020-02-10 22:16:19 +00:00
|
|
|
public readonly TimeSpan UtcOffset;
|
2017-10-29 10:19:09 +00:00
|
|
|
/// <summary>
|
|
|
|
/// True if UTC offset was found in the string
|
|
|
|
/// </summary>
|
2020-02-10 22:16:19 +00:00
|
|
|
public readonly bool IsUtcOffsetFound;
|
2017-10-29 10:19:09 +00:00
|
|
|
/// <summary>
|
|
|
|
/// Utc gotten from DateTime if IsUtcOffsetFound is True
|
|
|
|
/// </summary>
|
|
|
|
public DateTime UtcDateTime;
|
|
|
|
|
2020-03-14 16:05:10 +00:00
|
|
|
internal ParsedDateTime(int indexOfDate, int lengthOfDate, int indexOfTime, int lengthOfTime, DateTime dateTime)
|
2017-10-29 10:19:09 +00:00
|
|
|
{
|
2020-03-14 16:05:10 +00:00
|
|
|
IndexOfDate = indexOfDate;
|
|
|
|
LengthOfDate = lengthOfDate;
|
|
|
|
IndexOfTime = indexOfTime;
|
|
|
|
LengthOfTime = lengthOfTime;
|
|
|
|
DateTime = dateTime;
|
|
|
|
IsDateFound = indexOfDate > -1;
|
|
|
|
IsTimeFound = indexOfTime > -1;
|
2017-10-29 10:19:09 +00:00
|
|
|
UtcOffset = new TimeSpan(25, 0, 0);
|
|
|
|
IsUtcOffsetFound = false;
|
|
|
|
UtcDateTime = new DateTime(1, 1, 1);
|
|
|
|
}
|
|
|
|
|
2020-03-14 16:05:10 +00:00
|
|
|
internal ParsedDateTime(int indexOfDate, int lengthOfDate, int indexOfTime, int lengthOfTime, DateTime dateTime, TimeSpan utcOffset)
|
2017-10-29 10:19:09 +00:00
|
|
|
{
|
2020-03-14 16:05:10 +00:00
|
|
|
IndexOfDate = indexOfDate;
|
|
|
|
LengthOfDate = lengthOfDate;
|
|
|
|
IndexOfTime = indexOfTime;
|
|
|
|
LengthOfTime = lengthOfTime;
|
|
|
|
DateTime = dateTime;
|
|
|
|
IsDateFound = indexOfDate > -1;
|
|
|
|
IsTimeFound = indexOfTime > -1;
|
|
|
|
UtcOffset = utcOffset;
|
|
|
|
IsUtcOffsetFound = Math.Abs(utcOffset.TotalHours) < 12;
|
2017-10-29 10:19:09 +00:00
|
|
|
if (!IsUtcOffsetFound)
|
|
|
|
UtcDateTime = new DateTime(1, 1, 1);
|
|
|
|
else
|
|
|
|
{
|
2020-03-14 16:05:10 +00:00
|
|
|
if (indexOfDate < 0)//to avoid negative date exception when date is undefined
|
2017-10-29 10:19:09 +00:00
|
|
|
{
|
2020-03-14 16:05:10 +00:00
|
|
|
var ts = dateTime.TimeOfDay + utcOffset;
|
2017-10-29 10:19:09 +00:00
|
|
|
if (ts < new TimeSpan(0))
|
|
|
|
UtcDateTime = new DateTime(1, 1, 2) + ts;
|
|
|
|
else
|
|
|
|
UtcDateTime = new DateTime(1, 1, 1) + ts;
|
|
|
|
}
|
|
|
|
else
|
2020-03-14 16:05:10 +00:00
|
|
|
UtcDateTime = dateTime + utcOffset;
|
2017-10-29 10:19:09 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// Date that is accepted in the following cases:
|
|
|
|
/// - no date was parsed by TryParseDateOrTime();
|
|
|
|
/// - no year was found by TryParseDate();
|
2020-03-14 16:05:10 +00:00
|
|
|
/// It is ignored if DefaultDateIsNow = true was set after DefaultDate
|
2017-10-29 10:19:09 +00:00
|
|
|
/// </summary>
|
|
|
|
public static DateTime DefaultDate
|
|
|
|
{
|
|
|
|
set
|
|
|
|
{
|
|
|
|
_DefaultDate = value;
|
|
|
|
DefaultDateIsNow = false;
|
|
|
|
}
|
2020-03-14 16:05:10 +00:00
|
|
|
get => DefaultDateIsNow ? DateTime.Now : _DefaultDate;
|
2017-10-29 10:19:09 +00:00
|
|
|
}
|
2020-02-10 22:16:19 +00:00
|
|
|
|
|
|
|
private static DateTime _DefaultDate = DateTime.Now;
|
2017-10-29 10:19:09 +00:00
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// If true then DefaultDate property is ignored and DefaultDate is always DateTime.Now
|
|
|
|
/// </summary>
|
|
|
|
public static bool DefaultDateIsNow = true;
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// Defines default date-time format.
|
|
|
|
/// </summary>
|
2020-03-14 16:05:10 +00:00
|
|
|
[Flags]
|
2017-10-29 10:19:09 +00:00
|
|
|
public enum DateTimeFormat
|
|
|
|
{
|
|
|
|
/// <summary>
|
|
|
|
/// month number goes before day number
|
|
|
|
/// </summary>
|
2020-03-14 16:05:10 +00:00
|
|
|
UsaDate,
|
2017-10-29 10:19:09 +00:00
|
|
|
/// <summary>
|
|
|
|
/// day number goes before month number
|
|
|
|
/// </summary>
|
2020-03-14 16:05:10 +00:00
|
|
|
UkDate,
|
2017-10-29 10:19:09 +00:00
|
|
|
///// <summary>
|
|
|
|
///// time is specifed through AM or PM
|
|
|
|
///// </summary>
|
|
|
|
//USA_TIME,
|
|
|
|
}
|
|
|
|
|
|
|
|
#endregion
|
|
|
|
|
|
|
|
#region parsing derived methods for DateTime output
|
|
|
|
|
|
|
|
/// <summary>
|
2020-03-14 16:05:10 +00:00
|
|
|
/// Tries to find date and time within the passed string and return it as DateTime structure.
|
2017-10-29 10:19:09 +00:00
|
|
|
/// </summary>
|
|
|
|
/// <param name="str">string that contains date and/or time</param>
|
2020-03-14 16:05:10 +00:00
|
|
|
/// <param name="defaultFormat">format to be used preferably in ambivalent instances</param>
|
|
|
|
/// <param name="dateTime">parsed date-time output</param>
|
2017-10-29 10:19:09 +00:00
|
|
|
/// <returns>true if both date and time were found, else false</returns>
|
2020-03-14 16:05:10 +00:00
|
|
|
public static bool TryParseDateTime(this string str, DateTimeFormat defaultFormat, out DateTime dateTime)
|
2017-10-29 10:19:09 +00:00
|
|
|
{
|
2020-03-14 16:05:10 +00:00
|
|
|
if (!TryParseDateTime(str, defaultFormat, out ParsedDateTime parsedDateTime))
|
2017-10-29 10:19:09 +00:00
|
|
|
{
|
2020-03-14 16:05:10 +00:00
|
|
|
dateTime = new DateTime(1, 1, 1);
|
2017-10-29 10:19:09 +00:00
|
|
|
return false;
|
|
|
|
}
|
2020-03-14 16:05:10 +00:00
|
|
|
dateTime = parsedDateTime.DateTime;
|
2017-10-29 10:19:09 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
/// <summary>
|
2020-03-14 16:05:10 +00:00
|
|
|
/// Tries to find date and/or time within the passed string and return it as DateTime structure.
|
2017-10-29 10:19:09 +00:00
|
|
|
/// If only date was found, time in the returned DateTime is always 0:0:0.
|
|
|
|
/// If only time was found, date in the returned DateTime is DefaultDate.
|
|
|
|
/// </summary>
|
|
|
|
/// <param name="str">string that contains date and(or) time</param>
|
2020-03-14 16:05:10 +00:00
|
|
|
/// <param name="defaultFormat">format to be used preferably in ambivalent instances</param>
|
|
|
|
/// <param name="dateTime">parsed date-time output</param>
|
2017-10-29 10:19:09 +00:00
|
|
|
/// <returns>true if date and/or time was found, else false</returns>
|
2020-03-14 16:05:10 +00:00
|
|
|
public static bool TryParseDateOrTime(this string str, DateTimeFormat defaultFormat, out DateTime dateTime)
|
2017-10-29 10:19:09 +00:00
|
|
|
{
|
2020-03-14 16:05:10 +00:00
|
|
|
if (!TryParseDateOrTime(str, defaultFormat, out ParsedDateTime parsedDateTime))
|
2017-10-29 10:19:09 +00:00
|
|
|
{
|
2020-03-14 16:05:10 +00:00
|
|
|
dateTime = new DateTime(1, 1, 1);
|
2017-10-29 10:19:09 +00:00
|
|
|
return false;
|
|
|
|
}
|
2020-03-14 16:05:10 +00:00
|
|
|
dateTime = parsedDateTime.DateTime;
|
2017-10-29 10:19:09 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
/// <summary>
|
2020-03-14 16:05:10 +00:00
|
|
|
/// Tries to find time within the passed string and return it as DateTime structure.
|
2017-10-29 10:19:09 +00:00
|
|
|
/// It recognizes only time while ignoring date, so date in the returned DateTime is always 1/1/1.
|
|
|
|
/// </summary>
|
|
|
|
/// <param name="str">string that contains time</param>
|
2020-03-14 16:05:10 +00:00
|
|
|
/// <param name="defaultFormat">format to be used preferably in ambivalent instances</param>
|
2017-10-29 10:19:09 +00:00
|
|
|
/// <param name="time">parsed time output</param>
|
|
|
|
/// <returns>true if time was found, else false</returns>
|
2020-03-14 16:05:10 +00:00
|
|
|
public static bool TryParseTime(this string str, DateTimeFormat defaultFormat, out DateTime time)
|
2017-10-29 10:19:09 +00:00
|
|
|
{
|
2020-03-14 16:05:10 +00:00
|
|
|
if (!TryParseTime(str, defaultFormat, out var parsedTime, null))
|
2017-10-29 10:19:09 +00:00
|
|
|
{
|
|
|
|
time = new DateTime(1, 1, 1);
|
|
|
|
return false;
|
|
|
|
}
|
2020-03-14 16:05:10 +00:00
|
|
|
time = parsedTime.DateTime;
|
2017-10-29 10:19:09 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
/// <summary>
|
2020-03-14 16:05:10 +00:00
|
|
|
/// Tries to find date within the passed string and return it as DateTime structure.
|
2017-10-29 10:19:09 +00:00
|
|
|
/// It recognizes only date while ignoring time, so time in the returned DateTime is always 0:0:0.
|
2020-03-14 16:05:10 +00:00
|
|
|
/// If year of the date was not found then it accepts the current year.
|
2017-10-29 10:19:09 +00:00
|
|
|
/// </summary>
|
|
|
|
/// <param name="str">string that contains date</param>
|
2020-03-14 16:05:10 +00:00
|
|
|
/// <param name="defaultFormat">format to be used preferably in ambivalent instances</param>
|
2017-10-29 10:19:09 +00:00
|
|
|
/// <param name="date">parsed date output</param>
|
|
|
|
/// <returns>true if date was found, else false</returns>
|
2020-03-14 16:05:10 +00:00
|
|
|
public static bool TryParseDate(this string str, DateTimeFormat defaultFormat, out DateTime date)
|
2017-10-29 10:19:09 +00:00
|
|
|
{
|
2020-03-14 16:05:10 +00:00
|
|
|
if (!TryParseDate(str, defaultFormat, out ParsedDateTime parsedDate))
|
2017-10-29 10:19:09 +00:00
|
|
|
{
|
|
|
|
date = new DateTime(1, 1, 1);
|
|
|
|
return false;
|
|
|
|
}
|
2020-03-14 16:05:10 +00:00
|
|
|
date = parsedDate.DateTime;
|
2017-10-29 10:19:09 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
#endregion
|
|
|
|
|
|
|
|
#region parsing derived methods for ParsedDateTime output
|
|
|
|
|
|
|
|
/// <summary>
|
2020-03-14 16:05:10 +00:00
|
|
|
/// Tries to find date and time within the passed string and return it as ParsedDateTime object.
|
2017-10-29 10:19:09 +00:00
|
|
|
/// </summary>
|
|
|
|
/// <param name="str">string that contains date-time</param>
|
2020-03-14 16:05:10 +00:00
|
|
|
/// <param name="defaultFormat">format to be used preferably in ambivalent instances</param>
|
|
|
|
/// <param name="parsedDateTime">parsed date-time output</param>
|
2017-10-29 10:19:09 +00:00
|
|
|
/// <returns>true if both date and time were found, else false</returns>
|
2020-03-14 16:05:10 +00:00
|
|
|
public static bool TryParseDateTime(this string str, DateTimeFormat defaultFormat, out ParsedDateTime parsedDateTime)
|
2017-10-29 10:19:09 +00:00
|
|
|
{
|
2020-03-14 16:05:10 +00:00
|
|
|
if (TryParseDateOrTime(str, defaultFormat, out parsedDateTime)
|
|
|
|
&& parsedDateTime.IsDateFound
|
|
|
|
&& parsedDateTime.IsTimeFound
|
2017-10-29 10:19:09 +00:00
|
|
|
)
|
|
|
|
return true;
|
|
|
|
|
2020-03-14 16:05:10 +00:00
|
|
|
parsedDateTime = null;
|
2017-10-29 10:19:09 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
|
|
|
/// <summary>
|
2020-03-14 16:05:10 +00:00
|
|
|
/// Tries to find time within the passed string and return it as ParsedDateTime object.
|
2017-10-29 10:19:09 +00:00
|
|
|
/// It recognizes only time while ignoring date, so date in the returned ParsedDateTime is always 1/1/1
|
|
|
|
/// </summary>
|
|
|
|
/// <param name="str">string that contains date-time</param>
|
2020-03-14 16:05:10 +00:00
|
|
|
/// <param name="defaultFormat">format to be used preferably in ambivalent instances</param>
|
|
|
|
/// <param name="parsedTime">parsed date-time output</param>
|
2017-10-29 10:19:09 +00:00
|
|
|
/// <returns>true if time was found, else false</returns>
|
2020-03-14 16:05:10 +00:00
|
|
|
public static bool TryParseTime(this string str, DateTimeFormat defaultFormat, out ParsedDateTime parsedTime)
|
|
|
|
=> TryParseTime(str, defaultFormat, out parsedTime, null);
|
2017-10-29 10:19:09 +00:00
|
|
|
|
|
|
|
/// <summary>
|
2020-03-14 16:05:10 +00:00
|
|
|
/// Tries to find date and/or time within the passed string and return it as ParsedDateTime object.
|
2017-10-29 10:19:09 +00:00
|
|
|
/// If only date was found, time in the returned ParsedDateTime is always 0:0:0.
|
|
|
|
/// If only time was found, date in the returned ParsedDateTime is DefaultDate.
|
|
|
|
/// </summary>
|
|
|
|
/// <param name="str">string that contains date-time</param>
|
2020-03-14 16:05:10 +00:00
|
|
|
/// <param name="defaultFormat">format to be used preferably in ambivalent instances</param>
|
|
|
|
/// <param name="parsedDateTime">parsed date-time output</param>
|
2017-10-29 10:19:09 +00:00
|
|
|
/// <returns>true if date or time was found, else false</returns>
|
2020-03-14 16:05:10 +00:00
|
|
|
public static bool TryParseDateOrTime(this string str, DateTimeFormat defaultFormat, out ParsedDateTime parsedDateTime)
|
2017-10-29 10:19:09 +00:00
|
|
|
{
|
2020-03-14 16:05:10 +00:00
|
|
|
parsedDateTime = null;
|
2017-10-29 10:19:09 +00:00
|
|
|
|
2020-03-14 16:05:10 +00:00
|
|
|
ParsedDateTime parsedTime;
|
|
|
|
if (!TryParseDate(str, defaultFormat, out
|
|
|
|
ParsedDateTime parsedDate))
|
2017-10-29 10:19:09 +00:00
|
|
|
{
|
2020-03-14 16:05:10 +00:00
|
|
|
if (!TryParseTime(str, defaultFormat, out parsedTime, null))
|
2017-10-29 10:19:09 +00:00
|
|
|
return false;
|
|
|
|
|
2020-03-14 16:05:10 +00:00
|
|
|
var dateTime = new DateTime(DefaultDate.Year, DefaultDate.Month, DefaultDate.Day, parsedTime.DateTime.Hour, parsedTime.DateTime.Minute, parsedTime.DateTime.Second);
|
|
|
|
parsedDateTime = new ParsedDateTime(-1, -1, parsedTime.IndexOfTime, parsedTime.LengthOfTime, dateTime, parsedTime.UtcOffset);
|
2017-10-29 10:19:09 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2020-03-14 16:05:10 +00:00
|
|
|
if (!TryParseTime(str, defaultFormat, out parsedTime, parsedDate))
|
2017-10-29 10:19:09 +00:00
|
|
|
{
|
2020-03-14 16:05:10 +00:00
|
|
|
var dateTime = new DateTime(parsedDate.DateTime.Year, parsedDate.DateTime.Month, parsedDate.DateTime.Day, 0, 0, 0);
|
|
|
|
parsedDateTime = new ParsedDateTime(parsedDate.IndexOfDate, parsedDate.LengthOfDate, -1, -1, dateTime);
|
2017-10-29 10:19:09 +00:00
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2020-03-14 16:05:10 +00:00
|
|
|
var dateTime = new DateTime(parsedDate.DateTime.Year, parsedDate.DateTime.Month, parsedDate.DateTime.Day, parsedTime.DateTime.Hour, parsedTime.DateTime.Minute, parsedTime.DateTime.Second);
|
|
|
|
parsedDateTime = new ParsedDateTime(parsedDate.IndexOfDate, parsedDate.LengthOfDate, parsedTime.IndexOfTime, parsedTime.LengthOfTime, dateTime, parsedTime.UtcOffset);
|
2017-10-29 10:19:09 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
#endregion
|
|
|
|
|
|
|
|
#region parsing base methods
|
|
|
|
|
|
|
|
/// <summary>
|
|
|
|
/// Tries to find time within the passed string (relatively to the passed parsed_date if any) and return it as ParsedDateTime object.
|
|
|
|
/// It recognizes only time while ignoring date, so date in the returned ParsedDateTime is always 1/1/1
|
|
|
|
/// </summary>
|
|
|
|
/// <param name="str">string that contains date</param>
|
2020-03-14 16:05:10 +00:00
|
|
|
/// <param name="defaultFormat">format to be used preferably in ambivalent instances</param>
|
|
|
|
/// <param name="parsedTime">parsed date-time output</param>
|
|
|
|
/// <param name="parsedDate">ParsedDateTime object if the date was found within this string, else NULL</param>
|
2017-10-29 10:19:09 +00:00
|
|
|
/// <returns>true if time was found, else false</returns>
|
2020-03-14 16:05:10 +00:00
|
|
|
public static bool TryParseTime(this string str, DateTimeFormat defaultFormat, out ParsedDateTime parsedTime, ParsedDateTime parsedDate)
|
2017-10-29 10:19:09 +00:00
|
|
|
{
|
2020-03-14 16:05:10 +00:00
|
|
|
parsedTime = null;
|
2017-10-29 10:19:09 +00:00
|
|
|
|
2020-03-14 16:05:10 +00:00
|
|
|
var timeZoneR = defaultFormat == DateTimeFormat.UsaDate ?
|
|
|
|
@"(?:\s*(?'time_zone'UTC|GMT|CST|EST))?" : @"(?:\s*(?'time_zone'UTC|GMT))?";
|
2017-10-29 10:19:09 +00:00
|
|
|
|
|
|
|
Match m;
|
2020-03-14 16:05:10 +00:00
|
|
|
if (parsedDate != null && parsedDate.IndexOfDate > -1)
|
2017-10-29 10:19:09 +00:00
|
|
|
{//look around the found date
|
2020-03-14 16:05:10 +00:00
|
|
|
//look for <date> hh:mm:ss <UTC offset>
|
|
|
|
m = Regex.Match(str.Substring(parsedDate.IndexOfDate + parsedDate.LengthOfDate), @"(?<=^\s*,?\s+|^\s*at\s*|^\s*[T\-]\s*)(?'hour'\d{2})\s*:\s*(?'minute'\d{2})\s*:\s*(?'second'\d{2})\s+(?'offset_sign'[\+\-])(?'offset_hh'\d{2}):?(?'offset_mm'\d{2})(?=$|[^\d\w])", RegexOptions.Compiled);
|
2017-10-29 10:19:09 +00:00
|
|
|
if (!m.Success)
|
2020-03-14 16:05:10 +00:00
|
|
|
//look for <date> [h]h:mm[:ss] [PM/AM] [UTC/GMT]
|
|
|
|
m = Regex.Match(str.Substring(parsedDate.IndexOfDate + parsedDate.LengthOfDate), @"(?<=^\s*,?\s+|^\s*at\s*|^\s*[T\-]\s*)(?'hour'\d{1,2})\s*:\s*(?'minute'\d{2})\s*(?::\s*(?'second'\d{2}))?(?:\s*(?'ampm'AM|am|PM|pm))?" + timeZoneR + @"(?=$|[^\d\w])", RegexOptions.Compiled);
|
2017-10-29 10:19:09 +00:00
|
|
|
if (!m.Success)
|
|
|
|
//look for [h]h:mm:ss [PM/AM] [UTC/GMT] <date>
|
2020-03-14 16:05:10 +00:00
|
|
|
m = Regex.Match(str.Substring(0, parsedDate.IndexOfDate), @"(?<=^|[^\d])(?'hour'\d{1,2})\s*:\s*(?'minute'\d{2})\s*(?::\s*(?'second'\d{2}))?(?:\s*(?'ampm'AM|am|PM|pm))?" + timeZoneR + @"(?=$|[\s,]+)", RegexOptions.Compiled);
|
2017-10-29 10:19:09 +00:00
|
|
|
if (!m.Success)
|
|
|
|
//look for [h]h:mm:ss [PM/AM] [UTC/GMT] within <date>
|
2020-03-14 16:05:10 +00:00
|
|
|
m = Regex.Match(str.Substring(parsedDate.IndexOfDate, parsedDate.LengthOfDate), @"(?<=^|[^\d])(?'hour'\d{1,2})\s*:\s*(?'minute'\d{2})\s*(?::\s*(?'second'\d{2}))?(?:\s*(?'ampm'AM|am|PM|pm))?" + timeZoneR + @"(?=$|[\s,]+)", RegexOptions.Compiled);
|
2017-10-29 10:19:09 +00:00
|
|
|
}
|
|
|
|
else//look anywhere within string
|
|
|
|
{
|
2020-03-14 16:05:10 +00:00
|
|
|
//look for hh:mm:ss <UTC offset>
|
2017-10-29 10:19:09 +00:00
|
|
|
m = Regex.Match(str, @"(?<=^|\s+|\s*T\s*)(?'hour'\d{2})\s*:\s*(?'minute'\d{2})\s*:\s*(?'second'\d{2})\s+(?'offset_sign'[\+\-])(?'offset_hh'\d{2}):?(?'offset_mm'\d{2})?(?=$|[^\d\w])", RegexOptions.Compiled);
|
|
|
|
if (!m.Success)
|
|
|
|
//look for [h]h:mm[:ss] [PM/AM] [UTC/GMT]
|
2020-03-14 16:05:10 +00:00
|
|
|
m = Regex.Match(str, @"(?<=^|\s+|\s*T\s*)(?'hour'\d{1,2})\s*:\s*(?'minute'\d{2})\s*(?::\s*(?'second'\d{2}))?(?:\s*(?'ampm'AM|am|PM|pm))?" + timeZoneR + @"(?=$|[^\d\w])", RegexOptions.Compiled);
|
2017-10-29 10:19:09 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
if (!m.Success)
|
|
|
|
return false;
|
|
|
|
|
|
|
|
//try
|
|
|
|
//{
|
2020-02-10 22:16:19 +00:00
|
|
|
var hour = int.Parse(m.Groups["hour"].Value);
|
2017-10-29 10:19:09 +00:00
|
|
|
if (hour < 0 || hour > 23)
|
|
|
|
return false;
|
|
|
|
|
2020-02-10 22:16:19 +00:00
|
|
|
var minute = int.Parse(m.Groups["minute"].Value);
|
2017-10-29 10:19:09 +00:00
|
|
|
if (minute < 0 || minute > 59)
|
|
|
|
return false;
|
|
|
|
|
2020-02-10 22:16:19 +00:00
|
|
|
var second = 0;
|
2017-10-29 10:19:09 +00:00
|
|
|
if (!string.IsNullOrEmpty(m.Groups["second"].Value))
|
|
|
|
{
|
|
|
|
second = int.Parse(m.Groups["second"].Value);
|
|
|
|
if (second < 0 || second > 59)
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2020-03-14 16:05:10 +00:00
|
|
|
if ("PM".Equals(m.Groups["ampm"].Value, StringComparison.OrdinalIgnoreCase) && hour < 12)
|
2017-10-29 10:19:09 +00:00
|
|
|
hour += 12;
|
2020-03-14 16:05:10 +00:00
|
|
|
else if ("AM".Equals(m.Groups["ampm"].Value, StringComparison.OrdinalIgnoreCase) && hour == 12)
|
2017-10-29 10:19:09 +00:00
|
|
|
hour -= 12;
|
|
|
|
|
2020-03-14 16:05:10 +00:00
|
|
|
var dateTime = new DateTime(1, 1, 1, hour, minute, second);
|
2020-02-09 02:35:16 +00:00
|
|
|
|
2017-10-29 10:19:09 +00:00
|
|
|
if (m.Groups["offset_hh"].Success)
|
|
|
|
{
|
2020-03-14 16:05:10 +00:00
|
|
|
var offsetHh = int.Parse(m.Groups["offset_hh"].Value);
|
|
|
|
var offsetMm = 0;
|
2017-10-29 10:19:09 +00:00
|
|
|
if (m.Groups["offset_mm"].Success)
|
2020-03-14 16:05:10 +00:00
|
|
|
offsetMm = int.Parse(m.Groups["offset_mm"].Value);
|
|
|
|
var utcOffset = new TimeSpan(offsetHh, offsetMm, 0);
|
2017-10-29 10:19:09 +00:00
|
|
|
if (m.Groups["offset_sign"].Value == "-")
|
2020-03-14 16:05:10 +00:00
|
|
|
utcOffset = -utcOffset;
|
|
|
|
parsedTime = new ParsedDateTime(-1, -1, m.Index, m.Length, dateTime, utcOffset);
|
2017-10-29 10:19:09 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
if (m.Groups["time_zone"].Success)
|
|
|
|
{
|
2020-03-14 16:05:10 +00:00
|
|
|
TimeSpan utcOffset;
|
2017-10-29 10:19:09 +00:00
|
|
|
switch (m.Groups["time_zone"].Value)
|
|
|
|
{
|
|
|
|
case "UTC":
|
|
|
|
case "GMT":
|
2020-03-14 16:05:10 +00:00
|
|
|
utcOffset = new TimeSpan(0, 0, 0);
|
2017-10-29 10:19:09 +00:00
|
|
|
break;
|
|
|
|
case "CST":
|
2020-03-14 16:05:10 +00:00
|
|
|
utcOffset = new TimeSpan(-6, 0, 0);
|
2017-10-29 10:19:09 +00:00
|
|
|
break;
|
|
|
|
case "EST":
|
2020-03-14 16:05:10 +00:00
|
|
|
utcOffset = new TimeSpan(-5, 0, 0);
|
2017-10-29 10:19:09 +00:00
|
|
|
break;
|
|
|
|
default:
|
|
|
|
throw new Exception("Time zone: " + m.Groups["time_zone"].Value + " is not defined.");
|
|
|
|
}
|
2020-03-14 16:05:10 +00:00
|
|
|
parsedTime = new ParsedDateTime(-1, -1, m.Index, m.Length, dateTime, utcOffset);
|
2017-10-29 10:19:09 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
2020-03-14 16:05:10 +00:00
|
|
|
parsedTime = new ParsedDateTime(-1, -1, m.Index, m.Length, dateTime);
|
2017-10-29 10:19:09 +00:00
|
|
|
//}
|
|
|
|
//catch(Exception e)
|
|
|
|
//{
|
|
|
|
// return false;
|
|
|
|
//}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
/// <summary>
|
2020-03-14 16:05:10 +00:00
|
|
|
/// Tries to find date within the passed string and return it as ParsedDateTime object.
|
2017-10-29 10:19:09 +00:00
|
|
|
/// It recognizes only date while ignoring time, so time in the returned ParsedDateTime is always 0:0:0.
|
2020-03-14 16:05:10 +00:00
|
|
|
/// If year of the date was not found then it accepts the current year.
|
2017-10-29 10:19:09 +00:00
|
|
|
/// </summary>
|
|
|
|
/// <param name="str">string that contains date</param>
|
2020-03-14 16:05:10 +00:00
|
|
|
/// <param name="defaultFormat">format to be used preferably in ambivalent instances</param>
|
|
|
|
/// <param name="parsedDate">parsed date output</param>
|
2017-10-29 10:19:09 +00:00
|
|
|
/// <returns>true if date was found, else false</returns>
|
2020-03-14 16:05:10 +00:00
|
|
|
public static bool TryParseDate(this string str, DateTimeFormat defaultFormat, out ParsedDateTime parsedDate)
|
2017-10-29 10:19:09 +00:00
|
|
|
{
|
2020-03-14 16:05:10 +00:00
|
|
|
parsedDate = null;
|
2017-10-29 10:19:09 +00:00
|
|
|
|
|
|
|
if (string.IsNullOrEmpty(str))
|
|
|
|
return false;
|
|
|
|
|
|
|
|
//look for dd/mm/yy
|
2020-02-10 22:16:19 +00:00
|
|
|
var m = Regex.Match(str, @"(?<=^|[^\d])(?'day'\d{1,2})\s*(?'separator'[\\/\.])+\s*(?'month'\d{1,2})\s*\'separator'+\s*(?'year'\d{2}|\d{4})(?=$|[^\d])", RegexOptions.Compiled | RegexOptions.IgnoreCase);
|
2017-10-29 10:19:09 +00:00
|
|
|
if (m.Success)
|
|
|
|
{
|
|
|
|
DateTime date;
|
2020-03-14 16:05:10 +00:00
|
|
|
if ((defaultFormat ^ DateTimeFormat.UsaDate) == DateTimeFormat.UsaDate)
|
2017-10-29 10:19:09 +00:00
|
|
|
{
|
2020-03-14 16:05:10 +00:00
|
|
|
if (!ConvertToDate(int.Parse(m.Groups["year"].Value), int.Parse(m.Groups["day"].Value), int.Parse(m.Groups["month"].Value), out date))
|
2017-10-29 10:19:09 +00:00
|
|
|
return false;
|
|
|
|
}
|
|
|
|
else
|
|
|
|
{
|
2020-03-14 16:05:10 +00:00
|
|
|
if (!ConvertToDate(int.Parse(m.Groups["year"].Value), int.Parse(m.Groups["month"].Value), int.Parse(m.Groups["day"].Value), out date))
|
2017-10-29 10:19:09 +00:00
|
|
|
return false;
|
|
|
|
}
|
2020-03-14 16:05:10 +00:00
|
|
|
parsedDate = new ParsedDateTime(m.Index, m.Length, -1, -1, date);
|
2017-10-29 10:19:09 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
//look for [yy]yy-mm-dd
|
|
|
|
m = Regex.Match(str, @"(?<=^|[^\d])(?'year'\d{2}|\d{4})\s*(?'separator'[\-])\s*(?'month'\d{1,2})\s*\'separator'+\s*(?'day'\d{1,2})(?=$|[^\d])", RegexOptions.Compiled | RegexOptions.IgnoreCase);
|
|
|
|
if (m.Success)
|
|
|
|
{
|
2020-03-14 16:05:10 +00:00
|
|
|
if (!ConvertToDate(int.Parse(m.Groups["year"].Value), int.Parse(m.Groups["month"].Value), int.Parse(m.Groups["day"].Value), out var date))
|
2017-10-29 10:19:09 +00:00
|
|
|
return false;
|
2020-03-14 16:05:10 +00:00
|
|
|
parsedDate = new ParsedDateTime(m.Index, m.Length, -1, -1, date);
|
2017-10-29 10:19:09 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
//look for month dd yyyy
|
|
|
|
m = Regex.Match(str, @"(?:^|[^\d\w])(?'month'Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)[uarychilestmbro]*\s+(?'day'\d{1,2})(?:-?st|-?th|-?rd|-?nd)?\s*,?\s*(?'year'\d{4})(?=$|[^\d\w])", RegexOptions.Compiled | RegexOptions.IgnoreCase);
|
|
|
|
if (!m.Success)
|
|
|
|
//look for dd month [yy]yy
|
|
|
|
m = Regex.Match(str, @"(?:^|[^\d\w:])(?'day'\d{1,2})(?:-?st\s+|-?th\s+|-?rd\s+|-?nd\s+|-|\s+)(?'month'Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)[uarychilestmbro]*(?:\s*,?\s*|-)'?(?'year'\d{2}|\d{4})(?=$|[^\d\w])", RegexOptions.Compiled | RegexOptions.IgnoreCase);
|
|
|
|
if (!m.Success)
|
|
|
|
//look for yyyy month dd
|
|
|
|
m = Regex.Match(str, @"(?:^|[^\d\w])(?'year'\d{4})\s+(?'month'Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)[uarychilestmbro]*\s+(?'day'\d{1,2})(?:-?st|-?th|-?rd|-?nd)?(?=$|[^\d\w])", RegexOptions.Compiled | RegexOptions.IgnoreCase);
|
|
|
|
if (!m.Success)
|
|
|
|
//look for month dd hh:mm:ss MDT|UTC yyyy
|
|
|
|
m = Regex.Match(str, @"(?:^|[^\d\w])(?'month'Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)[uarychilestmbro]*\s+(?'day'\d{1,2})\s+\d{2}\:\d{2}\:\d{2}\s+(?:MDT|UTC)\s+(?'year'\d{4})(?=$|[^\d\w])", RegexOptions.Compiled | RegexOptions.IgnoreCase);
|
|
|
|
if (!m.Success)
|
|
|
|
//look for month dd [yyyy]
|
|
|
|
m = Regex.Match(str, @"(?:^|[^\d\w])(?'month'Jan|Feb|Mar|Apr|May|Jun|Jul|Aug|Sep|Oct|Nov|Dec)[uarychilestmbro]*\s+(?'day'\d{1,2})(?:-?st|-?th|-?rd|-?nd)?(?:\s*,?\s*(?'year'\d{4}))?(?=$|[^\d\w])", RegexOptions.Compiled | RegexOptions.IgnoreCase);
|
|
|
|
if (m.Success)
|
|
|
|
{
|
2020-02-10 22:16:19 +00:00
|
|
|
var month = -1;
|
2020-03-14 16:05:10 +00:00
|
|
|
var indexOfDate = m.Index;
|
|
|
|
var lengthOfDate = m.Length;
|
2017-10-29 10:19:09 +00:00
|
|
|
|
|
|
|
switch (m.Groups["month"].Value)
|
|
|
|
{
|
|
|
|
case "Jan":
|
|
|
|
case "JAN":
|
|
|
|
month = 1;
|
|
|
|
break;
|
|
|
|
case "Feb":
|
|
|
|
case "FEB":
|
|
|
|
month = 2;
|
|
|
|
break;
|
|
|
|
case "Mar":
|
|
|
|
case "MAR":
|
|
|
|
month = 3;
|
|
|
|
break;
|
|
|
|
case "Apr":
|
|
|
|
case "APR":
|
|
|
|
month = 4;
|
|
|
|
break;
|
|
|
|
case "May":
|
|
|
|
case "MAY":
|
|
|
|
month = 5;
|
|
|
|
break;
|
|
|
|
case "Jun":
|
|
|
|
case "JUN":
|
|
|
|
month = 6;
|
|
|
|
break;
|
|
|
|
case "Jul":
|
|
|
|
month = 7;
|
|
|
|
break;
|
|
|
|
case "Aug":
|
|
|
|
case "AUG":
|
|
|
|
month = 8;
|
|
|
|
break;
|
|
|
|
case "Sep":
|
|
|
|
case "SEP":
|
|
|
|
month = 9;
|
|
|
|
break;
|
|
|
|
case "Oct":
|
|
|
|
case "OCT":
|
|
|
|
month = 10;
|
|
|
|
break;
|
|
|
|
case "Nov":
|
|
|
|
case "NOV":
|
|
|
|
month = 11;
|
|
|
|
break;
|
|
|
|
case "Dec":
|
|
|
|
case "DEC":
|
|
|
|
month = 12;
|
|
|
|
break;
|
|
|
|
}
|
|
|
|
|
2020-03-14 16:05:10 +00:00
|
|
|
var year = !string.IsNullOrEmpty(m.Groups["year"].Value) ?
|
|
|
|
int.Parse(m.Groups["year"].Value) : DefaultDate.Year;
|
2017-10-29 10:19:09 +00:00
|
|
|
|
2020-03-14 16:05:10 +00:00
|
|
|
if (!ConvertToDate(year, month, int.Parse(m.Groups["day"].Value), out var date))
|
2017-10-29 10:19:09 +00:00
|
|
|
return false;
|
2020-03-14 16:05:10 +00:00
|
|
|
parsedDate = new ParsedDateTime(indexOfDate, lengthOfDate, -1, -1, date);
|
2017-10-29 10:19:09 +00:00
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
|
2020-03-14 16:05:10 +00:00
|
|
|
private static bool ConvertToDate(int year, int month, int day, out DateTime date)
|
2017-10-29 10:19:09 +00:00
|
|
|
{
|
|
|
|
if (year >= 100)
|
|
|
|
{
|
|
|
|
if (year < 1000)
|
|
|
|
{
|
|
|
|
date = new DateTime(1, 1, 1);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
else
|
|
|
|
if (year > 30)
|
2020-02-09 02:35:16 +00:00
|
|
|
year += 1900;
|
|
|
|
else
|
|
|
|
year += 2000;
|
2017-10-29 10:19:09 +00:00
|
|
|
|
|
|
|
try
|
|
|
|
{
|
|
|
|
date = new DateTime(year, month, day);
|
|
|
|
}
|
|
|
|
catch
|
|
|
|
{
|
|
|
|
date = new DateTime(1, 1, 1);
|
|
|
|
return false;
|
|
|
|
}
|
|
|
|
return true;
|
|
|
|
}
|
|
|
|
|
|
|
|
#endregion
|
|
|
|
}
|
2020-02-09 02:35:16 +00:00
|
|
|
}
|