结合连续的日期

时间:2015-05-14 04:42:38

标签: c# date

我有一个日期期间列表,这些期间不重叠

|StartDate| EndDate|
| null    |  1/12  |
| 2/12    |  null  |
| null    |  4/12  |
| 6/12    |  8/12  |
| 9/12    |  null  |
| null    |  10/12 |
| 11/12   |  null  |

我必须将这些句点组合成如下所示的列表:

|StartDate| EndDate|
| null    |  1/12  |
| 2/12    |  4/12  |
| 6/12    |  8/12  |
| 9/12    |  10/12 |
| 11/12   |  null  |

这是我的解决方案,但我认为这不是一个聪明的方式

        var dateList = periodList.SelectMany(x => new[] { 
            new {Date = x.Item1, type = "S"}, 
            new {Date = x.Item2, type = "E"} 
        }).Where(x => x.Date != null).OrderBy(x => x.Date).ToArray();

        var result = new List<Tuple<DateTime?, DateTime?>>();
        int i = 0;
        do
        {
            if (i == 0 && dateList[i].type == "E")
            {
                result.Add(new Tuple<DateTime?, DateTime?>(null, dateList[i].Date));
            }
            else if (i + 1 == dateList.Count() && dateList[i].type == "S")
            {
                result.Add(new Tuple<DateTime?, DateTime?>(dateList[i].Date, null));
            }
            else
            {
                if (dateList[i].type == "S" && dateList[i+1].type == "E")
                {
                    result.Add(new Tuple<DateTime?, DateTime?>(dateList[i].Date, dateList[i + 1].Date));
                    i++;
                }                    
            }
            i++;
        } while (i < dateList.Count());

4 个答案:

答案 0 :(得分:0)

我的解决方案似乎更长,但在我看来更清晰 。我认为您有期间类(而不是Tuple<DateTime?, DateTime?>),如下所示:

public class Period
{
    public DateTime? StartDate { get; set; }
    public DateTime? EndDate { get; set; }
}

将您的日期添加到期间列表中:

// Make list of periods ready
List<Period> periodList = //Prepare periods;

通过消除 null 分别获取 的开始日期和结束日期

// Get start dates
List<DateTime> startDates = periodList
    .Where(p => p.StartDate.HasValue)
    .Select(p => p.StartDate.Value)
    .ToList();

// Get end dates
List<DateTime> endDates = periodList
    .Where(p => p.EndDate.HasValue)
    .Select(p => p.EndDate.Value)
    .ToList();

然后做其他操作:

// Clear list of periods
periodList.Clear();

// Add start dates which are bigger than LAST end date with NULL end date period
startDates.Where(s => s > endDates.Max())
    .ToList()
    .ForEach(s => periodList.Add(new Period() { StartDate = s, EndDate = null }));

// Add end dates which are smaller than FIRST start date with NULL start date period
endDates.Where(e => e < startDates.Min())
    .ToList()
    .ForEach(e => periodList.Add(new Period() {StartDate = null, EndDate = e}));

// Match other dates and add them to list
startDates.Where(s => s < endDates.Max())
    .ToList()
    .ForEach(s => periodList.Add(new Period()
                                {
                                    StartDate = s,
                                    EndDate = endDates.Where(e => e > s).Min()
                                }));

// Oder period list
periodList = periodList.OrderBy(p => p.StartDate).ToList();

您可以测试.NET小提琴演示here

我希望它会有所帮助。

答案 1 :(得分:0)

以下代码是使用

的实现
  1. DateRange类型和
  2. DateTime.MinValueDateTime.MaxValue的标记值,以“保护”任何null首次开始日期或任何null最后结束日期。
  3. 以下是从可以为DateRange个值的两个并行列表中创建DateTime值列表的方法:

    public static List<DateRange> MakeRanges(List<DateTime?> list1, List<DateTime?> list2)
    {
        //Validate arguments to the function
        if (list1 == null || list2 == null || list1.Count == 0 ||
            list2.Count == 0 || list1.Count != list2.Count)
        {
            throw new ArgumentException("Bad arguments passed to MakeRanges().");
        }
        //If present, replace null start value of list1 with a sentinel
        list1[0] = list1[0] ?? DateTime.MinValue;
        //If present, replace null end value of list2 with a sentinel 
        list2[list2.Count - 1] = list2[list2.Count - 1] ?? DateTime.MaxValue;
    
        //this expression does the heavy lifting.  Match a start date with the closest non-null end-date
        return list1.Where(s => s.HasValue).Select( startItem => new DateRange{
                Start = startItem,
                End = list2.Find(e => (e.HasValue && (e > startItem.Value))).Value
            }).ToList();
    }
    

    这是一个清理任何标记的DateRange类:

    public class DateRange
    {
        private DateTime? _start;
        private DateTime? _end;
    
        public DateTime? Start
        { 
            get { return _start; }
            //get rid of any sentinel value
            set { _start = value == DateTime.MinValue ? null : value; } 
        }
    
        public DateTime? End
        { 
            get { return _end; }
            // get rid of any sentinel value
            set { _end = value == DateTime.MaxValue ? null : value; }
        } 
        //For checking results
        public override string ToString()
        {
            return String.Format("{0}-{1}", Start.HasValue ? Start.Value.ToShortDateString() : "null", End.HasValue ? End.Value.ToShortDateString() : "null");
        }
    }
    

    这是一个Console App Main作为驱动程序:

    public static void Main(string[] args)
    {
        List<DateTime?> list1 = new List<DateTime?>() { null, DateTime.Parse("2014-02-12"), null, DateTime.Parse("2014-06-12"), DateTime.Parse("2014-09-12"), null, DateTime.Parse("2014-11-12")};
        List<DateTime?> list2 = new List<DateTime?>() { DateTime.Parse("2014-01-12"), null, DateTime.Parse("2014-04-12"), DateTime.Parse("2014-08-12"), null, DateTime.Parse("2014-10-12"), null };
    
        List<DateRange> ranges = MakeRanges(list1, list2);
    
        //Print out results
        foreach (var range in ranges)
        {
            Console.WriteLine(range);
        }
        var pressAKeyToExit = Console.ReadKey();
    }
    

    输出是:

    null-1/12/2014
    2/12/2014-4/12/2014
    6/12/2014-8/12/2014
    9/12/2014-10/12/2014
    11/12/2014-null
    

答案 2 :(得分:0)

假设日期总是在输入列表中按顺序排列,以下可以是一个快速解决方案(不要知道它是否更快):

using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;

namespace SO30229368
{
    class Program
    {
        private const string NullItem = "null";

        static void Main(string[] args)
        {
            var periodList = new List<Tuple<string, string>>
            {
                new Tuple<string, string>(NullItem, "1/12"),
                new Tuple<string, string>("2/12", NullItem),
                new Tuple<string, string>(NullItem, "4/12"),
                new Tuple<string, string>("6/12", "8/12"),
                new Tuple<string, string>("9/12", NullItem),
                new Tuple<string, string>(NullItem, "10/12"),
                new Tuple<string, string>("11/12", NullItem)
            };

            var consecutiveList = GetConsecutive(periodList);
            foreach (var tupleItem in consecutiveList)
                Console.WriteLine("{0} - {1}", tupleItem.Item1, tupleItem.Item2);

            Console.ReadLine();
        }

        private static IEnumerable<Tuple<string, string>> GetConsecutive(List<Tuple<string, string>> periodList)
        {
            if (periodList == null)
                throw new ArgumentNullException("periodList");
            if (periodList.Count == 0)
                return new List<Tuple<string, string>>();

            var startList = periodList.Select(x => x.Item1).Where(y => y != NullItem).ToList();
            if (periodList.First().Item1 == NullItem)
                startList.Insert(0, NullItem);
            var endList = periodList.Select(x => x.Item2).Where(y => y != NullItem).ToList();
            if (periodList.Last().Item2 == NullItem)
                endList.Add(NullItem);

            Debug.Assert(startList.Count == endList.Count);
            return Enumerable.Zip(startList, endList, (start, end) => new Tuple<string, string>(start, end)).ToList();
        }
    }
}

虽然按照其他人的建议,最好将对象建模为自定义对象(例如DateRange),而不是使用元组。

答案 3 :(得分:0)

如果你确定

1)日期间隔按顺序

2)在每个null endDate之后,下一个startDate将为null

然后一个简单的方法可能是删除所有中间List<DateTime?> startDates = allStartDates(); // excluding null values, but including the first one. List<DateTime?> endDates = allEndDates(); // excluding null values, but including the last one. 值。

DateTime? startDate3 = startDates[2];
DateTime? endDate3 = endDates[2];

然后你可以进行一对一的比赛以获得所有时间间隔。例如;

.../sdk/platform-tools/adb install -r .../build/outputs/apk/app-debug.apk