2010-09-24 43 views
49

Nhập: chuỗi có ngày và giờ tùy chọn. Các biểu diễn khác nhau sẽ tốt đẹp nhưng cần thiết. Các chuỗi được cung cấp bởi người dùng và có thể không đúng định dạng. Ví dụ:Cách phân tích ngày/giờ từ chuỗi?

  • "2004-03-21 12:45:33" (tôi xem xét việc này cách bố trí mặc định)
  • "2004/03/21 12:45:33" (tùy chọn bố trí)
  • "23.09.2004 04:12:21" (định dạng Đức, không bắt buộc)
  • "2003-02-11" (thời gian có thể bị thiếu)

Đầu ra cần thiết: Giây từ Epoch (1970/01/01 00:00:00) hoặc một số điểm cố định khác.

Tiền thưởng: Ngoài ra, việc đọc UTC-offset của thời gian hệ thống cục bộ sẽ tuyệt vời.

Đầu vào được giả định là giờ địa phương trên máy được đề cập. Đầu ra cần phải là UTC. Hệ thống chỉ là Linux (Debian Lenny và Ubuntu cần thiết).

Tôi đã cố sử dụng boost/date_time, nhưng phải thừa nhận rằng tôi không thể bao quanh đầu tài liệu. Các công trình sau đây mà không cần phải chuyển đổi từ hệ thống theo giờ địa phương để UTC:

std::string date = "2000-01-01"; 
boost::posix_time::ptime ptimedate = boost::posix_time::time_from_string(date); 
ptimedate += boost::posix_time::hours(Hardcoded_UTC_Offset);// where to get from? 
struct tm = boost::posix_time::to_tm(ptimedate); 
int64_t ticks = mktime(&mTmTime); 

Tôi nghĩ boost::date_time có thể cung cấp UTC cần bù đắp, nhưng tôi sẽ không biết làm thế nào.

+1

Tôi tin rằng bạn sẽ phải phân tích chúng theo cách riêng của bạn (có lẽ với tinh thần) vì số tháng đơn trong "2004-3-21" không được phân tích bởi bất kỳ thời gian tăng định dạng IO nào http: //www.boost.org/doc/libs/1_44_0/doc/html/date_time/date_time_io.html#date_time.format_flags – Cubbi

+0

@Cubbi: nếu đó là vấn đề duy nhất, sẽ dễ dàng hơn nhiều để kiểm tra điều đó và chèn 0 vào chuỗi hơn là mang tinh thần vào bức tranh. –

+0

@Cubbi - bạn có thể xử lý các định dạng đầu vào và đầu ra tùy chỉnh trong 'boost :: date_time' -' boost :: spirit' quá mức ở đây –

Trả lời

59

Mặc dù tôi không biết cách định dạng đầu vào tháng một chữ số để tăng, tôi có thể thực hiện sau khi chỉnh sửa hai chữ số:

#include <iostream> 
#include <boost/date_time.hpp> 
namespace bt = boost::posix_time; 
const std::locale formats[] = { 
std::locale(std::locale::classic(),new bt::time_input_facet("%Y-%m-%d %H:%M:%S")), 
std::locale(std::locale::classic(),new bt::time_input_facet("%Y/%m/%d %H:%M:%S")), 
std::locale(std::locale::classic(),new bt::time_input_facet("%d.%m.%Y %H:%M:%S")), 
std::locale(std::locale::classic(),new bt::time_input_facet("%Y-%m-%d"))}; 
const size_t formats_n = sizeof(formats)/sizeof(formats[0]); 

std::time_t pt_to_time_t(const bt::ptime& pt) 
{ 
    bt::ptime timet_start(boost::gregorian::date(1970,1,1)); 
    bt::time_duration diff = pt - timet_start; 
    return diff.ticks()/bt::time_duration::rep_type::ticks_per_second; 

} 
void seconds_from_epoch(const std::string& s) 
{ 
    bt::ptime pt; 
    for(size_t i=0; i<formats_n; ++i) 
    { 
     std::istringstream is(s); 
     is.imbue(formats[i]); 
     is >> pt; 
     if(pt != bt::ptime()) break; 
    } 
    std::cout << " ptime is " << pt << '\n'; 
    std::cout << " seconds from epoch are " << pt_to_time_t(pt) << '\n'; 
} 
int main() 
{ 
    seconds_from_epoch("2004-03-21 12:45:33"); 
    seconds_from_epoch("2004/03/21 12:45:33"); 
    seconds_from_epoch("23.09.2004 04:12:21"); 
    seconds_from_epoch("2003-02-11"); 
} 

lưu ý rằng giây-từ-kỷ nguyên sản lượng sẽ được giả định ngày là vào UTC:

~ $ ./test | head -2 
ptime is 2004-Mar-21 12:45:33 
seconds from epoch are 1079873133 
~ $ date -d @1079873133 
Sun Mar 21 07:45:33 EST 2004 

Bạn có thể có thể sử dụng boost::posix_time::c_time::localtime() từ #include <boost/date_time/c_time.hpp> để có được chuyển đổi này thực hiện giả định đầu vào nằm trong múi giờ hiện tại, nhưng nó không phù hợp: ví dụ, kết quả sẽ khác nhau giữa ngày hôm nay và tháng sau, khi tiết kiệm ánh sáng ban ngày kết thúc.

+1

Hiển thị cách làm việc với các khía cạnh được đánh giá cao. Sử dụng localtime không phải là một tùy chọn nếu tôi hiểu nó ngay vì nó sẽ cho tôi giá trị bù đắp DST ngày hôm nay thay vì ngày đã cho. –

+0

@Gabriel Schreiber: Có thể bạn có thể thực hiện DST-offset vào ngày đã cho, bằng cách làm ngược lại với 'utc_to_local()' trong '/ usr/include/boost/date_time/c_local_time_adjustor.hpp', vẫn sẽ sử dụng vùng máy tính hiện tại.Một cách tốt hơn có thể là một cái gì đó gần gũi hơn với http://www.boost.org/doc/libs/1_44_0/doc/html/date_time/examples.html#date_time.examples.seconds_since_epoch – Cubbi

1

các giải pháp di động đơn giản nhất là sử dụng scanf:

int year, month, day, hour, minute, second = 0; 
int r = 0; 

r = scanf ("%d-%d-%d %d:%d:%d", &year, &month, &day, 
      &hour, &minute, &second); 
if (r == 6) 
{ 
    printf ("%d-%d-%d %d:%d:%d\n", year, month, day, hour, minute, 
      second); 
} 
else 
{ 
    r = scanf ("%d/%d/%d %d:%d:%d", &year, &month, &day, 
      &hour, &minute, &second); 
    // and so on ... 

Khởi tạo một struct tm với int giá trị và vượt qua nó để mktime để có được một thời gian lịch như time_t. Để chuyển đổi múi giờ, vui lòng see information trên gmtime.

+7

Thời gian chạy C 'scanf/printf' giới thiệu các vấn đề về quản lý bộ đệm và loại an toàn có thể tránh được bằng cách sử dụng các thư viện C++ thích hợp. –

+0

Điều này không giải quyết được vấn đề ở địa phương. Ngoài ra, chuỗi này do người dùng cung cấp và có thể không hợp lệ/không đúng định dạng. Tôi nghĩ rằng điều này có thể là một vấn đề với scanf? –

+0

@Gabriel nếu chuỗi không đúng định dạng, scanf sẽ không trả về 6. về utc, tôi đã thêm một số thông tin khác vào câu trả lời. –

8

boost::gregorian có một số thứ bạn cần mà không cần bạn làm bất kỳ công việc nhiều hơn:

using namespace boost::gregorian; 
{ 
    // The following date is in ISO 8601 extended format (CCYY-MM-DD) 
    std::string s("2000-01-01"); 
    date d(from_simple_string(s)); 
    std::cout << to_simple_string(d) << std::endl; 
} 

Có một ví dụ về cách sử dụng UTC offsets với boost::posix_timehere.

Bạn có thể cung cấp ngày và giờ từ các định dạng chuỗi đầu vào tùy chỉnh bằng cách sử dụng date_input_facettime_input_facet. Có một hướng dẫn I/O trên this page sẽ giúp bạn bắt đầu.

+1

Thx cho facet/hướng dẫn linx. Sử dụng boost :: gregorian không giải quyết được vấn đề vì nó không cung cấp phân tích/biểu diễn thời gian. –

+0

@Gabriel - phải, bạn sẽ phải xây dựng trình phân tích cú pháp và trình định dạng của riêng bạn bằng cách sử dụng các công cụ này để xử lý tất cả các trường hợp bắt buộc của bạn. Trừ khi bạn có các định dạng đầu vào có thể không bị ràng buộc, có thể sử dụng trình phân tích cú pháp cho từng định dạng và trình bao bọc xác định loại định dạng và nguồn cấp dữ liệu cho trình phân tích cú pháp thích hợp. –

+0

@Gabriel - lưu ý rằng khi tôi nói phân tích cú pháp, điều này thực sự không có gì phức tạp cho các tùy chọn 'chuỗi' đầu vào của bạn. Chỉ cần phát hiện và xây dựng các cấu trúc Boost thích hợp để phân tích cú pháp một cách thích hợp vào một date_time. –

6

Nếu c-phong cách có thể chấp nhận: strptime() là con đường để đi, bởi vì bạn có thể chỉ định định dạng và có thể mất locale trong tài khoản:

tm brokenTime; 
strptime(str.c_str(), "%Y-%m-%d %T", &brokenTime); 
time_t sinceEpoch = timegm(brokenTime); 

bố trí khác nhau sẽ phải được kiểm tra với giá trị trả lại (nếu có thể). Múi giờ sẽ phải được thêm vào bằng cách kiểm tra đồng hồ hệ thống (localtime_r() với time(), tm_zone)

+0

strptime đã được thử. Nó không được chấp nhận bởi vì nó sẽ vui vẻ sụp đổ nếu chuỗi không được hình thành tốt. –

+0

Tôi sử dụng nó, nó không sụp đổ ở đây, nhưng kinh nghiệm có thể khác nhau. Tôi sẽ phải điều tra (để google) để chắc chắn ... – stefaanv

+0

@Gabriel: Ngoại trừ MacOs X Leopard, nơi strptime dường như bị hỏng, không có gì đặc biệt được tìm thấy (getdate crashes, Qalculate remove strptime (2004)). Bạn có thể cung cấp một số thông tin về hệ thống mà nó bị treo không? – stefaanv