Find first and last week days according to locale
Asked Answered
I

2

9

I'm trying to find first and last week days based on the locale.

In U.S.A. a week normally starts on Sunday, but in other countries it could start on another day - e.g. Monday or even Saturday.

setlocale(LC_ALL, "en_US.UTF-8");
date_default_timezone_set("America/New_York");

$start_week = (new DateTimeImmutable());
$start_week = $start_week->modify('this week');
$end_week = $start_week->modify('this week +6 days');

$interval = new DateInterval('P1D');
$week_range = new DatePeriod($start_week, $interval, $end_week);

foreach($week_range as $week_day) {
  // $week_day starts with Monday, supposed to be Sunday
}
Infantile answered 30/11, 2016 at 16:39 Comment(3)
'Start of week' on calendars is very often a user preference on top of a cultural default. Even if you're living in the US, you may wish your calendar display to start weeks on a Monday.Spiffy
@Leith: My Windows, Outlook, iPhone calendars all start on Sunday. Developers of those calendars didn't have such excuse.Infantile
And yet your Google calendar will let you pick. I'm not saying it's not a good idea to have a default based on locale - I'm just saying you don't necessarily want to set it in stone.Spiffy
S
10

One way to do this is to use the IntlCalendar class. It has a method getFirstDayofWeek(), which returns an integer, corresponding to the DOW_ constants in IntlCalendar:

const integer DOW_SUNDAY = 1 ;
const integer DOW_MONDAY = 2 ;
const integer DOW_TUESDAY = 3 ;
const integer DOW_WEDNESDAY = 4 ;
const integer DOW_THURSDAY = 5 ;
const integer DOW_FRIDAY = 6 ;
const integer DOW_SATURDAY = 7 ;

Use that to add days to the start day when calling DateTimeImmutable::modify() for the start day. See it in action with three locales (i.e. en_US, es_ES, sw_KE) in this phpfiddle.

$locale = 'es_ES'; //Spain Spanish locale
$cal1 = IntlCalendar::createInstance(NULL, $locale);
$firstDayOfWeek = $cal1->getFirstDayOfWeek();
$daysToAdd = $firstDayOfWeek - 2; //difference from US M-Sunday 
echo 'locale: '.$local.' first day of week: '.$cal1->getFirstDayOfWeek().' days to add: '.$daysToAdd.'<br />';
$start_week = new DateTimeImmutable();
$start_week = $start_week->modify('this week +'.$daysToAdd.' days');
$end_week = $start_week->modify('+6 days');
$interval = new DateInterval('P1D');
$week_range = new DatePeriod($start_week, $interval, $end_week);

foreach($week_range as $week_day) {
    echo 'week day: '.$week_day->format('l m/d/Y').'<br />';
}
Subequatorial answered 13/12, 2016 at 0:31 Comment(4)
I think this is the only native way to do it in PHP: IntlCalendar uses ICU, which in turn uses the robust CLDR database. DateTime, on the other hand, uses the Olson zoneinfo database, which doesn't contain start of week information.Ambulatory
Good solution. But how do I avoid using relative times like 'this week'. I need to use my $start_week object later. In other words I want to be able to determine first day of week at any date of the object (i.e. $start_week = $start_week->setTimestamp($my_ts)).Infantile
Hmm does that mean $my_ts would have various dates (e.g. 07/04/1776) and we would need to know what day of the week that is? So then would we need to find the difference between that day of the week number and the local first day of the week? Can you outline a scenario?Seleneselenious
Basically I just needed an object to manipulate... $intl_cal = IntlCalendar::createInstance(NULL, $locale); $days_to_add = $intl_cal->getFirstDayOfWeek() - 2; $first_day_of_week = (new DateTimeImmutable())->setTime(00,00,00)->modify('this week +' . $days_to_add . ' days')->format('l'); // Sunday. Thanks.Infantile
T
0

IntlCalendar can do all that, but it's not well documented.

Other answers use some hardcoded values, this doesn't.

$date = new DateTime('now');
$locale = 'en_US';

$thisWeek = IntlCalendar::fromDateTime($date, $locale);
$thisWeek->set(IntlCalendar::FIELD_DAY_OF_WEEK, $thisWeek->getFirstDayOfWeek());
// $thisWeek now points to the first day of the week
$weekStart = $thisWeek->toDateTime();

$daysToAdvance = $thisWeek->getMaximum(IntlCalendar::FIELD_DAY_OF_WEEK) - 1;
// Maximum number of days in a week minus 1 gets you to the last day
$weekEnd = $weekStart->modify("+{$daysToAdvance} days");

$previousWeek = IntlCalendar::fromDateTime($date, $locale);
$previousWeek->add(IntlCalendar::FIELD_WEEK_OF_YEAR, -1);
$previousWeek = $previousWeek->toDateTime();

$nextWeek = IntlCalendar::fromDateTime($date, $locale);
$nextWeek->add(IntlCalendar::FIELD_WEEK_OF_YEAR, 1);
$nextWeek = $nextWeek->toDateTime();
Toots answered 20/1, 2023 at 13:1 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.