【问题标题】:Translate a list of time punches into time worked by hour in C#在 C# 中将时间打卡列表转换为按小时工作的时间
【发布时间】:2017-03-17 22:27:18
【问题描述】:

我有一个 List 对象,其中详细说明了工人的打卡时间,如下所示:

Person  Time     Action
Cindy   07:00    clockin
Jim     08:12    clockin
Cindy   11:15    startlunch
Cindy   12:12    endlunch
Jim     12:30    startlunch
Jim     12:55    endlunch
Cindy   15:30    clockout
Jim     17:00    clockout

当然真实数据是数百人,按部门等细分。

他们希望我创建一份报告,显示一天中每小时的工作量。最终结果应该是这样的:

Time   Worked
7-8      1
8-9      1.8
9-10     2
10-11    2
11-12    1.25
12-1     1.3
etc.

作为中间步骤,我已将一系列出拳转换为 WorkRecords 列表,它看起来像这样:

Person     In      Out
Cindy      07:00  11:15 
Jim        08:22  12:30
Cindy      12:12  15:30
Jim        12:55  17:00

我正在考虑循环遍历所有时间并测试每个 WorkRecord(如果它包含给定小时内的时间),但这似乎很麻烦。

有没有更优雅的方法来使用 lambda 或 linq 表达式将我的数据重新组织到我的最终产品中?它可以从原始数据或中间数据开始。感谢您查看这个谜题。

【问题讨论】:

    标签: c# linq list datetime lambda


    【解决方案1】:

    这是我对 LINQ 的尝试。

    我假设您的WorkRecord 是这样的。

        public  class WorkRecord
        {
            public readonly String name;
            public readonly DateTime StarTime;
            public readonly DateTime EndTime;
    
            public WorkRecord(string name, DateTime starTime, DateTime endTime)
            {
                this.name = name;
                StarTime = starTime;
                EndTime = endTime;
            }
    
            public WorkRecord(string name, string starTime, string endTime)
            {
                this.name = name;
                StarTime = DateTime.ParseExact(starTime, "HH:mm", null);
                EndTime = DateTime.ParseExact(endTime, "HH:mm", null);
            }
        }
    

    不清楚它们是否已经被预过滤为单日。我将尝试涵盖这两种情况(但仍然过于简单)

        public  class DayStats
        {
            public readonly int[] TotalMinutes = new int[24];
    
            public void AddWorkRecord(WorkRecord record)
            {
                // Note: this method doesn't handle case when EndTime is next day
                // handle "middle" hours, they are all full
                for (int hour = record.StarTime.Hour + 1; hour < record.EndTime.Hour; hour++)
                {
                    TotalMinutes[hour] += 60;
                }
                // handle first and last hours that might be not full
                if (record.StarTime.Hour == record.EndTime.Hour)
                {
                    TotalMinutes[record.StarTime.Hour] += record.EndTime.Minute - record.StarTime.Minute;
                }
                else
                {
                    TotalMinutes[record.StarTime.Hour] += 60 - record.StarTime.Minute;
                    TotalMinutes[record.EndTime.Hour] += record.EndTime.Minute;
                }
            }
    
            public string AsPrettyString()
            {
                return string.Join("\n", TotalMinutes
                    .Select((totalMinutes, hour) => string.Format("{0}-{1} {2}", hour, hour + 1, totalMinutes)));
            }
        }
    
        public class TimeCardAggregate
        {
            private readonly Dictionary<DateTime, DayStats> _days = new Dictionary<DateTime, DayStats>();
    
            public void AddWorkRecord(WorkRecord record)
            {
                // Note: this method doesn't handle case when EndTime is next day
                var date = record.StarTime.Date;
                DayStats dayStats;
                if (!_days.TryGetValue(date, out dayStats))
                {
                    dayStats = new DayStats();
                    _days.Add(date, dayStats);
                }
                dayStats.AddWorkRecord(record);
            }
    
            public List<KeyValuePair<DateTime, DayStats>> GetTimecard()
            {
                return _days.ToList().OrderBy(kv => kv.Key).ToList();
            }
        }
    

    DayStats 表示单日的汇总结果。 TimeCardAggregate 是几天的结果。大部分工作是通过两个类中的辅助方法AddWorkRecord 完成的,您可以从Aggregate LINQ 方法中使用它们。看看如何使用它们:

        static void Main(string[] args)
        {
            List<WorkRecord> records = new List<WorkRecord>
            {
                new WorkRecord("Cindy", "07:00", "11:15"),
                new WorkRecord("Jim", "08:22", "12:30"),
                new WorkRecord("Jim", "12:12", "15:30"),
                new WorkRecord("Cindy", "12:55", "17:00")
            };
    
            var dayStats = records.Aggregate(new DayStats(), (ds, wr) =>
            {
                ds.AddWorkRecord(wr);
                return ds;
            });
    
            Console.WriteLine(dayStats.AsPrettyString());
    
    
    
            List<WorkRecord> recordsForTwoDays = new List<WorkRecord>();
            recordsForTwoDays.AddRange(records);
            // just copy the data for the next day
            recordsForTwoDays.AddRange(records.Select(wr => new WorkRecord(wr.name, wr.StarTime.AddDays(1), wr.StarTime.AddDays(1))));
            var timecard = recordsForTwoDays.Aggregate(new TimeCardAggregate(), (ds, wr) =>
            {
                ds.AddWorkRecord(wr);
                return ds;
            });
            Console.WriteLine("\n\n");
    
    
            Console.WriteLine(string.Join("\n-------------\n", timecard.GetTimecard().Select(kv =>
            {
                return kv.Key.ToShortDateString() + ":\n" + dayStats.AsPrettyString();
            })));
        }
    

    另请注意,AddWorkRecord 的两种实现方式都很幼稚,并且无法处理有人通宵工作并在几天内产生记录的情况。不过修复起来并不难。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2018-08-17
      • 2012-03-28
      • 2019-11-26
      • 2012-04-09
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2019-08-20
      相关资源
      最近更新 更多