четверг, 16 декабря 2010 г.

yyyy.MM.dd HH.mm.ss -> time_t

Поскольку time_t это время (UTC) в секундах, прошедшее с 1 января 1970 года, 00:00:00, то переход от System.DateTime к time_t легко осуществить следующим образом:

DateTime time = DateTime.UtcNow; //предположим что это вермя которое мы хотим сконвертить к time_t
DateTime unixEpochMidnight = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
long time_t = (long)(time - unixEpochMidnight).TotalSeconds;


* This source code was highlighted with Source Code Highlighter.


Однако недавно передо мной стала задача в том, чтобы разобрать строчку с простым представлением даты и времени, что-то типа "2010-12-15 14:30:00", сконвертировать ее в time_t, но сделать все это нужно было в unmanaged коде на C++. В том чтобы распарсить строку нет ничего сложного. Проблема только в том, чтобы из года, месяца, дня, часа, минут, секунд перейти к time_t. Сложность встает благодаря наличию високосных годов, однако она оказалась довольно просто решаемой.
Ниже приведен C# код программы осуществляющей преобразование к time_t. Почему не C++? Потому, что мне было удобней сначало решить задачку на языке который я лучше знаю, а потом портировать решение на С++

using System;

namespace TimeCs
{

  class Program
  {

    const long SecPerDay = 3600 * 24;
    const long A1 = 365 * SecPerDay; //=365 * 86400
    const long A2 = SecPerDay / 4; //=21600
    const long A3 = -SecPerDay / 100; //=864 Обратите внимание на занк "-"
    const long A4 = SecPerDay / 400; //=216
    const long SecPerYear = A1 + A2 + A3 + A4;


    static long SubYearsToSec(int y1, int y2)
    {
      long dif1 = SecPerYear * y1 / SecPerDay;
      long dif2 = SecPerYear * y2 / SecPerDay;
      return (dif1 - dif2) * SecPerDay;
    }
    

    static bool IsLeapYear(int year)
    {
      if (year % 400 == 0)
        return true;

      if (year % 100 == 0)
        return false;

      return year % 4 == 0;       
    }
    

    //month 1..12
    //day 1..31
    static int ToDayInYear(int year, int month, int day)
    { 
      int daysInFeb = IsLeapYear(year) ? 29 : 28;
      int[] daysInMonths = new int[] { 31, daysInFeb, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 };
      int stopIdx = month - 1; //1..12 -> 0..11
      int ax = 0;
      
      for (int i=0; i<stopIdx; ++i)
      {
        ax += daysInMonths[i];
      }      
      ax += day;

      return ax;
    }


    static long SubMonthsDaysToSec(int y1, int m1, int d1, int y2, int m2, int d2)
    {
      long dd1 = ToDayInYear(y1, m1, d1);
      long dd2 = ToDayInYear(y2, m2, d2);
      
      return (dd1 - dd2) * SecPerDay;
    }


    static long Sub(DateTime dt1, DateTime dt2)
    {
      long dYearsSec = SubYearsToSec(dt1.Year, dt2.Year);
      long dDaysSec = SubMonthsDaysToSec(dt1.Year, dt1.Month, dt1.Day, dt2.Year, dt2.Month, dt2.Day);
      long dTime = ((long)(dt1.Hour - dt2.Hour) * 60L + (long)(dt1.Minute - dt2.Minute)) * 60L + (long)(dt1.Second - dt2.Second);
      
      return dYearsSec + dDaysSec + dTime;
    }
    
    
    static long ToTime_T(DateTime dateTime)
    {
      DateTime unixEpochTime = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
      
      return Sub(dateTime, unixEpochTime);
    }


    static void Main(string[] args)
    {
      DateTime now = new DateTime(2010, 12, 15, 14, 48, 30, DateTimeKind.Utc);
      long time_t = ToTime_T(now);
      Console.WriteLine("time_t = {0}", time_t);
      
      TimeSpan span = now - new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
      long referenceTime = (long)span.TotalSeconds;
      Console.WriteLine("reference time_t = {0}", referenceTime);
    }
    
  } //class
} //namespace


* This source code was highlighted with Source Code Highlighter.


Замечу, что в программе есть места, которые можно несколько улучшить, в частности функцию ToDayInYear можно было бы написать и без использования цикла, но предпочту предоставить это читателю.

Комментариев нет:

Отправить комментарий