// converts a date/time string to a DateTime object using a GoLang layout
        public static DateTime ParseDateTimeGoLang(string date, string layout)
        {
            date = ParseUtil.NormalizeSpace(date);
            var pattern = layout;

            // year
            pattern = pattern.Replace("2006", "yyyy");
            pattern = pattern.Replace("06", "yy");

            // month
            pattern = pattern.Replace("January", "MMMM");
            pattern = pattern.Replace("Jan", "MMM");
            pattern = pattern.Replace("01", "MM");

            // day
            pattern = pattern.Replace("Monday", "dddd");
            pattern = pattern.Replace("Mon", "ddd");
            pattern = pattern.Replace("02", "dd");
            //pattern = pattern.Replace("_2", ""); // space padding not supported nativly by C#?
            pattern = pattern.Replace("2", "d");

            // hours/minutes/seconds
            pattern = pattern.Replace("05", "ss");

            pattern = pattern.Replace("15", "HH");
            pattern = pattern.Replace("03", "hh");
            pattern = pattern.Replace("3", "h");

            pattern = pattern.Replace("04", "mm");
            pattern = pattern.Replace("4", "m");

            pattern = pattern.Replace("5", "s");

            // month again
            pattern = pattern.Replace("1", "M");

            // fractional seconds
            pattern = pattern.Replace(".0000", "ffff");
            pattern = pattern.Replace(".000", "fff");
            pattern = pattern.Replace(".00", "ff");
            pattern = pattern.Replace(".0", "f");

            pattern = pattern.Replace(".9999", "FFFF");
            pattern = pattern.Replace(".999", "FFF");
            pattern = pattern.Replace(".99", "FF");
            pattern = pattern.Replace(".9", "F");

            // AM/PM
            pattern = pattern.Replace("PM", "tt");
            pattern = pattern.Replace("pm", "tt"); // not sure if this works

            // timezones
            // these might need further tuning
            //pattern = pattern.Replace("MST", "");
            //pattern = pattern.Replace("Z07:00:00", "");
            pattern = pattern.Replace("Z07:00", "'Z'zzz");
            pattern = pattern.Replace("Z07", "'Z'zz");
            //pattern = pattern.Replace("Z070000", "");
            //pattern = pattern.Replace("Z0700", "");
            pattern = pattern.Replace("Z07:00", "'Z'zzz");
            pattern = pattern.Replace("Z07", "'Z'zz");
            //pattern = pattern.Replace("-07:00:00", "");
            pattern = pattern.Replace("-07:00", "zzz");
            //pattern = pattern.Replace("-0700", "zz");
            pattern = pattern.Replace("-07", "zz");

            try
            {
                return(DateTime.ParseExact(date, pattern, CultureInfo.InvariantCulture));
            }
            catch (FormatException ex)
            {
                throw new FormatException($"Error while parsing DateTime \"{date}\", using layout \"{layout}\" ({pattern}): {ex.Message}");
            }
        }
        public static DateTime FromUnknown(string str, string format = null)
        {
            try
            {
                str = ParseUtil.NormalizeSpace(str);
                if (str.ToLower().Contains("now"))
                {
                    return(DateTime.Now);
                }

                // ... ago
                var match = _TimeAgoRegexp.Match(str);
                if (match.Success)
                {
                    var timeAgo = str;
                    return(FromTimeAgo(timeAgo));
                }

                // Today ...
                match = _TodayRegexp.Match(str);
                if (match.Success)
                {
                    var time = str.Replace(match.Groups[0].Value, "");
                    var dt   = DateTime.SpecifyKind(DateTime.UtcNow.Date, DateTimeKind.Unspecified);
                    dt += ParseTimeSpan(time);
                    return(dt);
                }

                // Yesterday ...
                match = _YesterdayRegexp.Match(str);
                if (match.Success)
                {
                    var time = str.Replace(match.Groups[0].Value, "");
                    var dt   = DateTime.SpecifyKind(DateTime.UtcNow.Date, DateTimeKind.Unspecified);
                    dt += ParseTimeSpan(time);
                    dt -= TimeSpan.FromDays(1);
                    return(dt);
                }

                // Tomorrow ...
                match = _TomorrowRegexp.Match(str);
                if (match.Success)
                {
                    var time = str.Replace(match.Groups[0].Value, "");
                    var dt   = DateTime.SpecifyKind(DateTime.UtcNow.Date, DateTimeKind.Unspecified);
                    dt += ParseTimeSpan(time);
                    dt += TimeSpan.FromDays(1);
                    return(dt);
                }

                // [day of the week] at ... (eg: Saturday at 14:22)
                match = _DaysOfWeekRegexp.Match(str);
                if (match.Success)
                {
                    var time = str.Replace(match.Groups[0].Value, "");
                    var dt   = DateTime.SpecifyKind(DateTime.UtcNow.Date, DateTimeKind.Unspecified);
                    dt += ParseTimeSpan(time);

                    DayOfWeek dow;
                    var       groupMatchLower = match.Groups[1].Value.ToLower();
                    if (groupMatchLower.StartsWith("monday"))
                    {
                        dow = DayOfWeek.Monday;
                    }
                    else if (groupMatchLower.StartsWith("tuesday"))
                    {
                        dow = DayOfWeek.Tuesday;
                    }
                    else if (groupMatchLower.StartsWith("wednesday"))
                    {
                        dow = DayOfWeek.Wednesday;
                    }
                    else if (groupMatchLower.StartsWith("thursday"))
                    {
                        dow = DayOfWeek.Thursday;
                    }
                    else if (groupMatchLower.StartsWith("friday"))
                    {
                        dow = DayOfWeek.Friday;
                    }
                    else if (groupMatchLower.StartsWith("saturday"))
                    {
                        dow = DayOfWeek.Saturday;
                    }
                    else
                    {
                        dow = DayOfWeek.Sunday;
                    }

                    while (dt.DayOfWeek != dow)
                    {
                        dt = dt.AddDays(-1);
                    }
                    return(dt);
                }

                try
                {
                    // try parsing the str as an unix timestamp
                    var unixTimeStamp = long.Parse(str);
                    return(UnixTimestampToDateTime(unixTimeStamp));
                }
                catch (FormatException)
                {
                    // it wasn't a timestamp, continue....
                }

                // add missing year
                match = _MissingYearRegexp.Match(str);
                if (match.Success)
                {
                    var date    = match.Groups[1].Value;
                    var newDate = DateTime.Now.Year + "-" + date;
                    str = str.Replace(date, newDate);
                }

                // add missing year 2
                match = _MissingYearRegexp2.Match(str);
                if (match.Success)
                {
                    var date = match.Groups[1].Value;
                    var time = match.Groups[2].Value;
                    str = date + " " + DateTime.Now.Year + " " + time;
                }

                return(FromFuzzyTime(str, format));
            }
            catch (Exception ex)
            {
                throw new Exception($"DateTime parsing failed for \"{str}\": {ex}");
            }
        }