2013-03-26 26 views
5

Tôi đang cố gắng tìm ra cách tốt nhất để chuyển đổi từ giây epoch (kể từ thời điểm NTP 1900-01-01 00:00) thành chuỗi ngày giờ (MM/DD/YY) , hh: mm: ss) không có bất kỳ thư viện/mô-đun/chức năng bên ngoài nào vì chúng không khả dụng trên thiết bị được nhúng.Chuyển đổi từ giây sang ngày trên thiết bị nhúng bị giới hạn

Suy nghĩ đầu tiên của tôi là xem Python datetime module source code, tuy nhiên điều đó không hữu ích đối với tôi.

Nỗ lực ban đầu của tôi bằng Python sử dụng chuyển đổi ngày kể từ 0001-01-01 cho đến ngày sử dụng getDateFromJulianDay được điều chỉnh cho Python từ C++ source, kết hợp với các hoạt động modulo để có thời gian. Nó hoạt động, nhưng có cách nào tốt hơn không?

def getDateFromJulianDay(julianDay): 
    # Gregorian calendar starting from October 15, 1582 
    # This algorithm is from: 
    # Henry F. Fliegel and Thomas C. van Flandern. 1968. 
    # Letters to the editor: 
    #  a machine algorithm for processing calendar dates. 
    # Commun. ACM 11, 10 (October 1968), 657-. DOI=10.1145/364096.364097 
    # http://doi.acm.org/10.1145/364096.364097 
    ell = julianDay + 68569; 
    n = (4 * ell)/146097; 
    ell = ell - (146097 * n + 3)/4; 
    i = (4000 * (ell + 1))/1461001; 
    ell = ell - (1461 * i)/4 + 31; 
    j = (80 * ell)/2447; 
    d = ell - (2447 * j)/80; 
    ell = j/11; 
    m = j + 2 - (12 * ell); 
    y = 100 * (n - 49) + i + ell; 
    return y,m,d 

# NTP response (integer portion) for Monday, March 25, 2013 at 6:40:43 PM 
sec_since_1900 = 3573225643 

# 2415021 is the number of days between 0001-01-01 and 1900-01-01, 
#  the start of the NTP epoch 
(year,month,day) = getDateFromJulianDay(2415021 + sec_since_1900/60/60/24) 

seconds_into_day = sec_since_1900 % 86400 
(hour, sec_past_hour) = divmod(seconds_into_day,3600) 
(min, sec) = divmod(sec_past_hour,60) 
print 'year:',year,'month:',month,'day:',day 
print 'hour:',hour,'min:',min,'sec:',sec 

Tại sao tôi đang làm điều này: Tôi nhận được thời gian hiện tại từ một máy chủ NTP, và dành thời gian này theo mệnh giá cho việc cập nhật một đồng hồ phần cứng thời gian thực (RTC) mà chỉ chấp nhận ngày, múi giờ và thời gian: MM/DD/YY, hh: mm: ss, ± zz. Tôi dự định triển khai các khả năng NTP thực sự vào một ngày sau đó. Thảo luận về các phương pháp đồng bộ hóa thời gian tốt nhất còn lại ở nơi khác, chẳng hạn như this question.

Ghi chú:

  • thiết bị nhúng của tôi là một Telit GC-864 modem di động chạy Python 1.5.2+ và chỉ có giới hạn khai thác (chủ yếu chỉ khai thác C), không có mô-đun, và một số các dự kiến được xây dựng trong các loại Python. Các khả năng chính xác là here, nếu bạn quan tâm. Tôi viết Python cho thiết bị này như thể tôi đang viết mã C - không phải là rất Pythonic, tôi biết.
  • Tôi nhận thấy NTP chỉ được sử dụng tốt nhất để bù đắp thời gian, tuy nhiên với các tùy chọn giới hạn, tôi sử dụng NTP làm nguồn thời gian tuyệt đối (tôi có thể thêm séc để chuyển khoản NTP vào năm 2036 để kích hoạt 136 năm hoạt động khác) .
  • Thiết bị GC-864-V2 với phần mềm cập nhật không có khả năng NTP, nhưng GC-864 tôi cần sử dụng bị kẹt trên bản phát hành chương trình cơ sở trước đó.
+0

Tại sao bạn bận tâm xử lý các ngày trước 1582? –

+0

Có, không cần thiết - được bao gồm trong mã gốc, quên bao gồm nguồn C++ của hàm getDateFromJulianDay, [dòng 132 tại đây] (http://qt.gitorious.org/qt/qt/blobs/4.7/src/ corelib/tools/qdatetime.cpp # line132). – swolpert

+0

Vì vậy, hãy xóa nó khỏi mã đã đăng của bạn và làm cho câu hỏi của bạn rõ ràng hơn. – dkamins

Trả lời

0

TL; DR

Nếu bạn đang sử dụng một Telit GC-864, trình thông dịch Python dường như chèn một số loại chậm trễ ở giữa mỗi dòng thực thi mã.

Đối với một Telit GC-864, hàm trong câu hỏi của tôi getDateFromJulianDay(julianDay) nhanh hơn hàm trong câu trả lời của tôi, ntp_time_to_date(ntp_time).

More Detail

Số mã dòng thống trị thời gian thực hiện trên GC-864 hơn phức tạp của mã - lạ, tôi biết. Hàm getDateFromJulianDay(julianDay) trong câu hỏi của tôi có một số thao tác phức tạp, có thể là 15 dòng mã. Các chức năng trong câu trả lời của tôi ntp_time_to_date(ntp_time) có độ phức tạp tính toán đơn giản hơn, nhưng while vòng gây ra 100 dòng thực thi mã:

đếm
  • Một vòng lặp từ 1900 đến năm hiện tại
  • Một đếm vòng lặp từ tháng 1 đến hiện tại tháng

Kết quả thi

kết quả xét nghiệm Thời gian chạy trên một thực tế GC-864 (lưu ý: không một GC-864-V2) sử dụng cùng một Đầu vào thời gian NTP cho mỗi lần thử (mỗi chức năng xuất ra "3/25/2013 18:40"). Thời gian được thực hiện bằng cách sử dụng debugging báo cáo printf, và một thiết bị đầu cuối nối tiếp trên một máy tính sẽ có thời gian đóng dấu mỗi dòng được gửi bởi GC-864.

getDateFromJulianDay(julianDay) Trials:

  • 0,3802 giây
  • 0,3370 giây
  • 0,3370 giây
  • trung bình: 0,3514 giây

ntp_time_to_date(ntp_time) Trials:

  • 0,8899 giây
  • 0,9072 giây
  • 0,8986 giây
  • trung bình: 0,8986 giây

Các biến đổi một phần bắt nguồn từ modem di động GC-864 phục vụ các nhiệm vụ mạng di động theo định kỳ.

Để hoàn chỉnh, tối ưu hóa việc nhập các biến số long thành int càng nhanh càng tốt trong ntp_time_to_date(ntp_time) có tác động khá đáng kể. Nếu không tối ưu hóa này:

  • 2,3155 giây
  • 1,5034 giây
  • 1,5293 giây
  • 2,0995 giây
  • 2,0909 giây
  • trung bình: 1,9255 giây

Làm bất cứ điều gì tính toán có liên quan trên một tệp Telit GC-864 đang chạy .pyo trong Python 1.5.2+ không phải là một ý tưởng tuyệt vời. Sử dụng GC-864-V2, có khả năng NTP tích hợp là giải pháp khả thi cho người gặp vấn đề này. Hơn nữa, các máy điện thoại di động (IPM) Internet đời mới của máy (IPT) có khả năng hơn nhiều.

Nếu bạn gặp sự cố tương tự với GC-864, hãy cân nhắc sử dụng modem điện thoại di động mới hơn và hiện đại hơn.

6

Các getDateFromJulianDay chức năng ban đầu được đề xuất là quá tính toán chuyên sâu để sử dụng hiệu quả trên một thiết bị nhúng, chứa nhiều hoạt động nhân và chia trên long biến lớn hoặc, như ban đầu được viết bằng C++, longlong variables.

Tôi nghĩ rằng tôi đã tìm kiếm một thuật toán hiệu quả cho đến ngày thuật toán cho thiết bị được nhúng.

Sau khi Googling không kết quả, tôi thấy mình trở lại trên Stack Overflow, và tìm thấy câu hỏi Converting epoch time to “real” date/time, hỏi về thời gian epoch tự viết để thực hiện ngày và cung cấp một thuật toán phù hợp. answer này cho câu hỏi tham chiếu gmtime.c source code, và cung cấp các nguồn trong C tôi cần phải viết một thuật toán chuyển đổi Python:

/* 
* gmtime - convert the calendar time into broken down time 
*/ 
/* $Header: /opt/proj/minix/cvsroot/src/lib/ansi/gmtime.c,v 1.1.1.1 2005/04/21 14:56:05 beng Exp $ */ 

#include  <time.h> 
#include  <limits.h> 
#include  "loc_time.h" 

struct tm * 
gmtime(register const time_t *timer) 
{ 
     static struct tm br_time; 
     register struct tm *timep = &br_time; 
     time_t time = *timer; 
     register unsigned long dayclock, dayno; 
     int year = EPOCH_YR; 

     dayclock = (unsigned long)time % SECS_DAY; 
     dayno = (unsigned long)time/SECS_DAY; 

     timep->tm_sec = dayclock % 60; 
     timep->tm_min = (dayclock % 3600)/60; 
     timep->tm_hour = dayclock/3600; 
     timep->tm_wday = (dayno + 4) % 7;  /* day 0 was a thursday */ 
     while (dayno >= YEARSIZE(year)) { 
       dayno -= YEARSIZE(year); 
       year++; 
     } 
     timep->tm_year = year - YEAR0; 
     timep->tm_yday = dayno; 
     timep->tm_mon = 0; 
     while (dayno >= _ytab[LEAPYEAR(year)][timep->tm_mon]) { 
       dayno -= _ytab[LEAPYEAR(year)][timep->tm_mon]; 
       timep->tm_mon++; 
     } 
     timep->tm_mday = dayno + 1; 
     timep->tm_isdst = 0; 

     return timep; 
} 

Thêm vào đó, analysis của câu hỏi Why is gmtime implemented this way? giúp khẳng định rằng gmtime chức năng là tương đối hiệu quả.

Sử dụng raspberryginger.com minix Doxygen documentation site, tôi có thể tìm thấy các macro và hằng số C được bao gồm trong gmtime.c từ loc_time.h.Đoạn mã có liên quan:

#define YEAR0   1900     /* the first year */ 
#define EPOCH_YR  1970   /* EPOCH = Jan 1 1970 00:00:00 */ 
#define SECS_DAY  (24L * 60L * 60L) 
#define LEAPYEAR(year) (!((year) % 4) && (((year) % 100) || !((year) % 400))) 
#define YEARSIZE(year) (LEAPYEAR(year) ? 366 : 365) 
#define FIRSTSUNDAY(timp)  (((timp)->tm_yday - (timp)->tm_wday + 420) % 7) 
#define FIRSTDAYOF(timp)  (((timp)->tm_wday - (timp)->tm_yday + 420) % 7) 
#define TIME_MAX  ULONG_MAX 
#define ABB_LEN   3 

extern const int _ytab[2][10]; 

extern const int _ytab được quy định tại misc.c:

const int _ytab[2][12] = { 
       { 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 }, 
       { 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31 } 
     }; 

Một số điều khác tôi thấy:

  • các gmtime.c File Reference là rất hữu ích cho việc tìm kiếm phụ thuộc.
  • Chức năng gmtime bắt đầu lập chỉ mục Tháng, Ngày trong tuần và Ngày trong năm ở số không, (phạm vi tối đa 0-11, 0-6, 0-365, tương ứng), trong khi Ngày trong tháng bắt đầu tại số 1, (1-31), xem IBM gmtime() reference.

Tôi lại viết gmtime chức năng cho Python 1.5.2+:

def is_leap_year(year): 
    return (not ((year) % 4) and (((year) % 100) or (not((year) % 400)))) 

def year_size(year): 
    if is_leap_year(year): 
     return 366 
    else: 
     return 365 

def ntp_time_to_date(ntp_time): 
    year = 1900   # EPOCH_YR for NTP 
    ytab = [ [ 31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31], 
       [ 31, 29, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31] ] 

    (dayno,dayclock) = divmod(ntp_time, 86400L) 
    dayno = int(dayno) 

    # Calculate time of day from seconds on the day's clock. 
    (hour, sec_past_hour) = divmod(dayclock,3600) 
    hour = int(hour) 
    (min, sec) = divmod(int(sec_past_hour),60) 

    while (dayno >= year_size(year)): 
     dayno = dayno - year_size(year) 
     year = year + 1 
    month = 1       # NOTE: month range is (1-12) 
    while (dayno >= ytab[is_leap_year(year)][month]): 
     dayno = dayno - ytab[is_leap_year(year)][month] 
     month = month + 1 
    day = dayno + 1 

    return (year, month, day, hour, min, sec) 

Modifications tôi khiến tái Sacombank chức năng C++ gmtime chức năng Python tôi ntp_time_to_date(ntp_time):

  • Đã thay đổi kỷ nguyên từ kỷ nguyên UNIX năm 1970 thành kỷ nguyên NTP năm 1900 (the prime epoch for NTP).
  • Thời gian tính theo ngày được sắp xếp hợp lý.
    • So sánh thời điểm tính ngày gmtime để ntp_time_to_date:
      • Cả (dayclock % 3600)/60dayclock/3600 xảy ra đằng sau hậu trường trong divmod(dayclock,3600)divmod(sec_past_hour,60).
      • Chỉ khác biệt thực sự là divmod(sec_past_hour,60) tránh modulo của dayclock (0-86.399) 60 qua dayclock % 60, và thay vào đó làm modulo của sec_past_hour (0-3599) 60 trong divmod(sec_past_hour,60).
  • Đã xóa biến và mã tôi không cần, ví dụ: ngày trong tuần.
  • Thay đổi chỉ mục của Tháng bắt đầu bằng 1, vì vậy Phạm vi tháng là (1-12) thay vì (0-11)
  • Nhập biến số từ long ngay sau khi giá trị nhỏ hơn 65535 để giảm đáng kể thực thi mã thời gian.
    • này đòi hỏi biến dài là:
      • ntp_time, giây kể từ 1900 (0-4294967295)
      • dayclock, giây vào ngày (0-86399)
    • lớn nhất của phần còn lại của các biến là năm được tính toán trong ngày.

Các Python ntp_time_to_date chức năng (với phụ thuộc của nó) chạy thành công trên Telit GC-864 trên một phiên bản nhúng của Python 1.5.2+, cũng như trên Python 2.7.3, nhưng tất nhiên sử dụng thư viện datetime nếu bạn có thể.

+0

Thật điên rồ với tôi rằng python phải là ngôn ngữ bạn sử dụng khi nền tảng bị hạn chế đến mức hàm đầu tiên quá chậm. Tôi đã đặt cược tiền mà không có gì giống như điều đó sẽ xảy ra trong thế giới thực, nhưng tôi đoán tôi đã sai! – cwa

+0

Bạn quan tâm đến giây nhảy vọt (lỗi ít hơn 40 giây, không đổi trước năm 1972 và chỉ hiển thị nếu bạn so sánh thời gian với đồng hồ thực (hoặc thời gian GPS) được đồng bộ hóa với quy mô UTC tại thời điểm đó) ? http://www.eecis.udel.edu/~mills/leap.html – jfs

+0

@sebastian: Tôi không quan tâm đến những giây nhảy vọt vào thời điểm này, nhưng điều đó chắc chắn là tốt để biết. Tôi đã không nhìn thấy bất kỳ offsets so với một thời gian đồng bộ hóa NTP của Linux (Ubuntu) hộp riêng biệt. Tài khoản đồng bộ hóa thời gian NTP Linux có cho giây nhảy vọt không? – swolpert

Các vấn đề liên quan