确定日期开始的时间 [英] Determining the time at which a date starts
问题描述
容易,对吧?从午夜开始,...错误!在美国/圣保罗,由于夏令时更改,每年有一天从01:00开始。
给定时区和日期,如何找到时代一天开始的时间?
我的第一个想法是使用以下内容,但假设每一天都有23:59。这可能不如假设每一天都有一个午夜。
perl -MDateTime -E'
说
DateTime-> new(year => 2013,month => 10,day => 20)
- > subtract(days => 1)
- > set(hour => 23,minute => 59)
- > set_time_zone(America / Sao_Paulo)
- > add(minutes => 1)
- > ; strftime(%H:%M);
'
01:00
有更强大或更直接的替代方法?
[此功能现已从 DateTimeX ::开始 ]
这里是一个仅使用DT公开的解决方案方法:
sub day_start {
my($ y,$ m,$ d,$ tz)= @_ ;
$ tz = DateTime :: TimeZone-> new(name => $ tz)
if!ref($ tz);
我的$ dt = DateTime-> new(year => $ y,month => $ m,day => $ d);
我的$ target_day =($ dt-> utc_rd_values)[0];
我的$ min_epoch = int($ dt-> epoch()/ 60) - 24 * 60;
my $ max_epoch = int($ dt-> epoch()/ 60)+ 24 * 60;
while($ max_epoch> $ min_epoch){
我的$ epoch =($ min_epoch + $ max_epoch)>> 1;
$ dt = DateTime-> from_epoch(epoch => $ epoch * 60,time_zone => $ tz);
if(($ dt-> local_rd_values)[0]< $ target_day){
$ min_epoch = $ epoch;
} else {
$ max_epoch = $ epoch;
}
}
返回DateTime-> from_epoch(epoch => $ max_epoch * 60,time_zone => $ tz);
}
由于大多数日期都有午夜,所以应该在顶部添加一个支票在不需要时绕过搜索。
假设:
- 没有dt可以添加时间以获得具有较早日期的dt。
- 在时区开始之前,日期开始时间不超过24 * 60 * 60秒。
- 在时区开始后,24小时不得超过24 * 60 * 60秒。
- 只发生在零秒的时间。 (优化)
测试:
{
#没有午夜。
my $ tz = DateTime :: TimeZone-> new(name =>'America / Sao_Paulo');
我的$ dt = day_start(2013,10,20,$ tz);
print($ dt-> epoch,,$ dt-> iso8601,\\\
); #1382238000 2013-10-20T01:00:00
$ dt-> subtract(seconds => 1);
print($ dt-> epoch,,$ dt-> iso8601,\\\
); #1382237999 2013-10-19T23:59:59
}
{
#两个半夜。
my $ tz = DateTime :: TimeZone-> new(name =>'America / Havana');
我的$ dt = day_start(2013,11,3,$ tz);
print($ dt-> epoch,,$ dt-> iso8601,\\\
); #1383451200 2013-11-03T00:00:00
$ dt-> subtract(seconds => 1);
print($ dt-> epoch,,$ dt-> iso8601,\\\
); #1383451199 2013-11-02T23:59:59
}
Say I want to create a daily planner, and I want to divide the day into 15 minute chunks.
Easy, right? Just start at midnight, and... Wrong! In America/Sao_Paulo, one day each year starts at 01:00 because of Daylight Saving Time changes.
Given a time zone and a date, how does one find the epoch time at which the day starts?
My first thought was to use the following, but it assumes each day has a 23:59. That's probably no better of an assumption than assuming each day has a midnight.
perl -MDateTime -E'
say
DateTime->new( year => 2013, month => 10, day => 20 )
->subtract( days => 1 )
->set( hour => 23, minute => 59 )
->set_time_zone("America/Sao_Paulo")
->add( minutes => 1 )
->strftime("%H:%M");
'
01:00
Is there a more robust or more direct alternative?
[This functionality is now available from DateTimeX::Start]
Here's a solution using only DT's public methods:
sub day_start {
my ($y, $m, $d, $tz) = @_;
$tz = DateTime::TimeZone->new( name => $tz )
if !ref($tz);
my $dt = DateTime->new( year => $y, month => $m, day => $d );
my $target_day = ( $dt->utc_rd_values )[0];
my $min_epoch = int($dt->epoch()/60) - 24*60;
my $max_epoch = int($dt->epoch()/60) + 24*60;
while ($max_epoch > $min_epoch) {
my $epoch = ( $min_epoch + $max_epoch ) >> 1;
$dt = DateTime->from_epoch( epoch => $epoch*60, time_zone => $tz );
if (( $dt->local_rd_values )[0] < $target_day) {
$min_epoch = $epoch;
} else {
$max_epoch = $epoch;
}
}
return DateTime->from_epoch(epoch => $max_epoch*60, time_zone => $tz);
}
Since most dates do have a midnight, a check should be added at the top to bypass the search when it's not needed.
Assumptions:
- There is no dt to which one can add time to obtain a dt with an earlier date.
- In no time zone does a date starts more than 24*60*60 seconds before the date starts in UTC.
- In no time zone does a date starts more than 24*60*60 seconds after the date starts in UTC.
- Jumps in time zones only occur on times with zero seconds. (Optimization)
Test:
{
# No midnight.
my $tz = DateTime::TimeZone->new( name => 'America/Sao_Paulo' );
my $dt = day_start(2013, 10, 20, $tz);
print($dt->epoch, " ", $dt->iso8601, "\n"); # 1382238000 2013-10-20T01:00:00
$dt->subtract( seconds => 1 );
print($dt->epoch, " ", $dt->iso8601, "\n"); # 1382237999 2013-10-19T23:59:59
}
{
# Two midnights.
my $tz = DateTime::TimeZone->new( name => 'America/Havana' );
my $dt = day_start(2013, 11, 3, $tz);
print($dt->epoch, " ", $dt->iso8601, "\n"); # 1383451200 2013-11-03T00:00:00
$dt->subtract( seconds => 1 );
print($dt->epoch, " ", $dt->iso8601, "\n"); # 1383451199 2013-11-02T23:59:59
}
这篇关于确定日期开始的时间的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!