Convert seconds to hhh:mm:ss in a chart

后端 未结 3 1855
南旧
南旧 2021-01-22 06:19

I have a MsSql database which calculates the timespan between two dates in seconds. That works fine. I use this column afterwards in C# and write them in an array.

This

相关标签:
3条回答
  • 2021-01-22 06:25

    If you are trying to show more than 2 digits of hour I think this should work for you

    //yourTimeSpan is the TimeSpan that you already have
    var hoursDouble = Math.Floor(yourTimeSpan.TotalHours);
    string hours;
    string minutes;
    string seconds;
    
    //check hours
    if(hoursDouble < 10)
    {
        hours = string.Format("0{0}", hoursDouble);
    }
    else
    {
        hours = hoursDouble.ToString();
    }
    //check minutes
    if (yourTimeSpan.Minutes < 10)
    {
        minutes = string.Format("0{0}", yourTimeSpan.Minutes);
    }
    else
    {
        minutes = yourTimeSpan.Minutes.ToString();
    }
    //check seconds
    if (yourTimeSpan.Seconds < 10)
    {
        seconds = string.Format("0{0}", yourTimeSpan.Seconds);
    }
    else
    {
        seconds = yourTimeSpan.Seconds.ToString();
    }
    
    string formattedSpan = String.Format("{0}:{1}:{2}", hours, minutes, seconds);
    

    Update: I think this should solve the problem you were seeing with single digit numbers

    0 讨论(0)
  • 2021-01-22 06:33

    Your task involves two parts:

    • displaying seconds in the hhh:mm:ss format
    • putting them as labels on the y-axis

    There is no suitable date-time formatting string for this in c#, so we can't make use of the built-in automatic labels and their formatting.

    There also no way to use expressions that call a function on the automatic labels, unfortunately.

    So we can't use those.

    Instead we will have to add CustomLabels. This is not very hard but does take a few steps..

    But let's start with a function that converts an int to the hhh:mm:ss string we want; this should do the job:

    string hhh_mm_ss(int seconds)
    {
        int sec = seconds % 60;
        int min = ((seconds - sec)/60) % 60;
        int hhh = (seconds - sec - 60 * min) / 3600;
        return hhh > 0 ? string.Format("{2}:{1:00}:{0:00}", sec, min, hhh) 
                       : min + ":" + sec.ToString("00");
    }
    

    Maybe it can be optimized, but for our purpose it'll do.

    Next we need to create the CustomLabels. They will replace the normal axis labels and we need to add them in a separate loop over the data after each binding.

    One special thing about them is their positioning. Which is smack between two values we need to give them: the FromPosition and ToPosition, both in the unit of the axis-values.

    Another difference to normal, automatic Labels is that it is up to us to create as many or few of them as we need..

    This function tries to create a number that will go up to the maximum y-value and space the CustomLabels at a given interval:

    void addCustomLabels(ChartArea ca, Series series, int interval)
    {
        // we get the maximum form the 1st y-value
        int max = (int)series.Points.Select(x => x.YValues[0]).Max();
        // we delete any CLs we have
        ca.AxisY.CustomLabels.Clear();
        // now we add new custom labels
        for (int i = 0; i < max; i += interval)
        {
            CustomLabel cl = new CustomLabel();
            cl.FromPosition = i - interval / 2;
            cl.ToPosition = i + interval / 2;
            cl.Text = hhh_mm_ss(i);
            ca.AxisY.CustomLabels.Add(cl);
        }
    }
    

    The first parameters to call this are obvious; the last one however is tricky:

    You need to decide to interval you want your labels to have. It will depend on various details of your chart:

    • the range of values
    • the size of the chart area
    • the size of the font of the axis

    I didn't set any special Font in the function; CustomLabels use the same Font as normal axis labels, i.e. AxisY.LabelStyle.Font.

    For my screenshot I prepared the chart like this:

    ca.AxisX.Minimum = 0;
    ca.AxisY.MajorTickMark.Interval = 60 * 60;  // one tick per hour
    addCustomLabels(ca, s, 60 * 30);            // one label every 30 minutes
    

    I have also added DataPoint Labels for testing to show the values..:

    series.Points[p].Label = hhh_mm_ss((int)y) + "\n" + y;
    

    Here is the result:

    0 讨论(0)
  • 2021-01-22 06:45

    UPDATE: This answer may be quite useful for other readers, but it pretty much misses the OP's issues. I'll leave it as it stands, but it will not help in creating specially formatted y-axis labels..


    Most Chart problems stem from invalid or useless x-values. The following discussion tries to help avoiding or getting around them..

    A number is a number and you can't simply display it as a DateTime, or for that matter a TimeSpan.

    So you need to add the X-Values as either DateTime or as double that contain values that can be converted to DateTime. The fomer is what I prefer..

    So instead of adding the seconds directly add them as offsets from a given DateTime:

    Change something like this

    series.Points.AddXY(sec, yValues);
    

    To this:

    var dt = new DateTime(0).AddSeconds(sec);
    series.Points.AddXY(dt, yValues);   
    

    Now you can use the date and time formatting strings as needed..:

    chartArea.AxisX.LabelStyle.Format = "{mm:ss}";
    

    You could also add them as doubles that actually are calculated from DateTimes via the ToOADate:

     series.Points.AddXY(dt.ToOADate(), yValues);
    

    But now you will have to set the ChartValueType.DateTime and probably also AxisX.IntervalType and AxisX.Interval to make sure the chart gets the formatting right..:

    s.XValueType = ChartValueType.DateTime;
    ca.AxisX.Interval = 5;
    ca.AxisX.IntervalType = DateTimeIntervalType.Seconds;
    ca.AxisX.LabelStyle.Format = "{mm:ss}";
    

    Pick values that suit your data!

    Note that the problem with your original code is that the X-Values internally always are doubles, but the seconds are not integer values in them but fractional parts; so you need some kind of calculation. That's what ToOADate does. Here is a short test that shows what one second actually does amount to as a OADate double :

    Best add the X-Values as DateTimes so all further processing can rely on the type..

    Update I just saw that you have finally added the real code to your question and that is uses Points.DataBindY. This will not create meaningful X-Values, I'm afraid. Try to switch to Points.DataBindXY! Of course the X-Values you bind to also need to follow the rules I have explained above..!

    You can do a loop over your array and convert the numbers like I shown above; here is a simple example:

        int[] seconds = new int[5] { 1, 3, 88, 123, 3333 };
        double[] oaSeconds = seconds.Select(x => new DateTime(0).AddSeconds(x).ToOADate())
                                    .ToArray();
    
    0 讨论(0)
提交回复
热议问题