Skip to main content

I'm looking for a programatic way to be able to set that setting in Date & Time. Any ideas?

The ability to use "Current Location" would be a great, but if it helps you as to what we do to set the timezone, I will gladly expound. We are all in the American Eastern Time Zone so I hardcode for "America/New_York"



We also have Network Time Servers at each of our locations. So to set both the timezone via command line on our images, I have a script that runs the following two commands:



systemsetup -settimezone "America/New_York"
systemsetup -setnetworktimeserver "yourntpserver.yourdomainhere.com"


So, now that we have the syntax of the commands covered, let me start the skeleton of a script for you...knowing that I lack the knowledge of how to finish this concept. If nothing else, this should give you some food for thought:



#!/bin/sh

###Variable Declaration###
#This is my main challenge. How do write a command(s) that will
#populate this variable by either pulling the network segment or
#Casper location data for a given Mac in the data base, and then how
#do we transform what is pulled from the database into something
#systemsetup understands such as 'America/New_York' or say 'Europe/Berlin'.

TimeZone="`<hoped for command the populates this variable here>`"

systemsetup -settimezone "$TimeZone"

exit 0


Hopefully some better scripters weigh in on this...and who knows...maybe there is a way to set it using "Current Location", but the man page for the systemsetup command does not cover that.


Further investigation on my 10.8.3 computer yielded that the actual file being touched is /Library/Preferences/com.apple.timezone.auto.plist I wonder if it's touching other files as well.



Time to fire up PlistBuddy to try to script it.


As always TEST TEST TEST in a dev environment first.
This works for me.
It only seems to do the first lookup after opening the Date & Time prefs.



#!/bin/sh

# Use "/usr/sbin/systemsetup -listtimezones" to see a list of available list time zones.
TimeZone="America/New_York"
TimeServer="time.apple.com"

############# Pause for network services #############
/bin/sleep 10
#################################################

/usr/sbin/systemsetup -setusingnetworktime off

#Set an initial time zone
/usr/sbin/systemsetup -settimezone $TimeZone

#Set specific time server
/usr/sbin/systemsetup -setnetworktimeserver $TimeServer

# enable location services
/bin/launchctl unload /System/Library/LaunchDaemons/com.apple.locationd.plist
uuid=`/usr/sbin/system_profiler SPHardwareDataType | grep "Hardware UUID" | cut -c22-57`
/usr/bin/defaults write /var/db/locationd/Library/Preferences/ByHost/com.apple.locationd.$uuid LocationServicesEnabled -int 1
/usr/sbin/chown -R _locationd:_locationd /var/db/locationd
/bin/launchctl load /System/Library/LaunchDaemons/com.apple.locationd.plist

# set time zone automatically using current location
/usr/bin/defaults write /Library/Preferences/com.apple.timezone.auto Active -bool true

/usr/sbin/systemsetup -setusingnetworktime on

/usr/sbin/systemsetup -gettimezone
/usr/sbin/systemsetup -getnetworktimeserver

exit 0

Just to answer my own question here's what I've come up with, pretty or elegant? No. Effective? Seems to be.



#!/bin/bash

uuid=/usr/sbin/system_profiler SPHardwareDataType | grep "Hardware UUID" | cut -c22-57

/bin/launchctl unload /System/Library/LaunchDaemons/com.apple.locationd.plist
/usr/bin/defaults write /private/var/db/locationd/Library/Preferences/ByHost/com.apple.locationd.$uuid LocationServicesEnabled -int 1
/usr/bin/defaults write /private/var/db/locationd/Library/Preferences/ByHost/com.apple.locationd.notbackedup.$uuid LocationServicesEnabled -int 1
/usr/sbin/chown -R _locationd:_locationd /var/db/locationd
/bin/launchctl load /System/Library/LaunchDaemons/com.apple.locationd.plist

exit 0


For Auto Time Zone similar idea



#!/bin/sh

/usr/bin/sudo /usr/bin/defaults write /Library/Preferences/com.apple.timezone.auto Active -bool True

exit 0

@ericbenfer Thanks for the TimeServer, TimeZone, Location Service on script. It was everything I needed!


I was actually looking at this myself. Am I right in that this only works after opening up Date and Time? There's got to be a way to kick off the "Determining location..." portion that the System Preference pane. I'll be looking a bit more into this, but figured I'd ask.


I just want to describe a few other things I tried in investigating this. I went ahead and implemented the script as is. Ran command in Terminal:



opensnoop -v


and then opened up Date and Time preferences



Lots of stuff going on but the main ones I saw were TimeZoneToAdmin, timezoned, locationd, and references to GeoKit.framework. Most of it was located in here: /System/Library/PreferencePanes/DateAndTime.prefPane/Contents/Resources/TimeZone.prefPane/Contents/Resources/timezoned.app



Unfortunately, I didn't get much further than that. Unloading and re-loading the plist doesn't seem to do much either. Tried looking at Developer documentation for anything related to timezone settings and there are references to Core Location services but I wouldn't know what to do with that at this point in time.



But here's what I discovered which I find rather interesting: if you disable and re-enable wireless it automatically re-syncs after a 2-3 seconds (this assumes it reconnects to a wireless network with an active internet connection). Doesn't work with Ethernet. I'm wondering if disabling the wireless and re-enabling the wireless as part of the script would automatically sync to the right timezone. The downside is if the computer only has a wireless connection and for whatever reason cannot reconnect to the network (and therefore the JSS) you may run into logs for the policy not completing successfully. Something to think about.


Running a simple commands of:



/usr/sbin/networksetup -setairportpower en0 off
/usr/sbin/networksetup -setairportpower en0 on


This assumes your wireless is en0. If it's not, you can run this command to determine what device name is listed as:



/usr/sbin/networksetup -listallhardwareports


Alternatively you can also use the Hardware Port name too:



/usr/sbin/networksetup -setairportpower Wi-Fi off
/usr/sbin/networksetup -setairportpower Wi-Fi on


But that may differ depending on the OS you're running. I think it was AirPort at one point and may have changed in either 10.7 or 10.8 to Wi-Fi.



Anyways this is all informational. I'll personally let OS naturally detect it either on a restart which a user is bound to do or when they connect to the wireless for the first time.


I found that this change would only be respected if the plist file was in xml format. Weird, huh?



This is my setup for enabling location-based time zone (Note; location services need to also be enabled, as above:



/bin/rm /Library/Preferences/com.apple.timezone.auto.plist
/usr/bin/defaults write /Library/Preferences/com.apple.timezone.auto Active -bool true
/usr/bin/plutil -convert xml1 /Library/Preferences/com.apple.timezone.auto.plist

What worked for me is to create a package using composer containing the com.apple.timezone.auto.plist set to true. I then pushed this out via policy and added it to our Casper Imaging configuration.



<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>Active</key>
<true/>
</dict>
</plist>


Thanks @ericbenfer ! Worked great for me.


So the script itself works for me but the problem that I'm seeing is that this will only set the timezone to Eastern but if the person sits in the Pacific timezone then it won't update the time on the machine until after they've launched the Date & Time preference pane. Is there any way to get the time zone to set itself correctly based off their geo automatically?


@cgiordano I ran into the same problem - we were previously unloading and loading the LaunchD located in /System/Library/LaunchDaemons/com.apple.locationd.plist to get the change to stick, but SIP in Sierra neutered that. Finally had some time to dig in today and found this little gem that forces the auto timezone if you've set all the other stuff properly for automatic timezone discovery and enabling Location Services.



Since the bulk of the auto timezone stuff is a shell script and the above is in Python, I just loaded it in as a heredoc after all the bash code:



/usr/bin/python << EOF
from Foundation import NSBundle
TZPP = NSBundle.bundleWithPath_("/System/Library/PreferencePanes/DateAndTime.prefPane/Contents/Resources/TimeZone.prefPane")
TimeZonePref = TZPP.classNamed_('TimeZonePref')
ATZAdminPrefererences = TZPP.classNamed_('ATZAdminPrefererences')

atzap = ATZAdminPrefererences.defaultPreferences()
pref = TimeZonePref.alloc().init()
atzap.addObserver_forKeyPath_options_context_(pref, "enabled", 0, 0)
result = pref._startAutoTimeZoneDaemon_(0x1)
EOF

Hey Guys!



Correct me if I'm wrong - the issue is not that the script doesn't work anymore. It's that Location Services have to be enabled for the automatic Date & Time function to work.



I am able to enable Location Services with the following bit of stuff in a 1st run script in the most up-to-date version macOS 10.12:



hdwruid=$(/usr/sbin/system_profiler SPHardwareDataType | /usr/bin/awk '/Hardware UUID/{print $NF}')
loginfo "Hardware UUID: $hdwruid"
loginfo "enabling Location Services..."
osref=$(/usr/bin/sw_vers -productVersion | /usr/bin/awk -F. '{print $2}')

locd_osx(){
/bin/rm -rf /private/var/db/locationd/Library/Preferences/ByHost/*
/usr/bin/defaults write /private/var/db/locationd/Library/Preferences/ByHost/com.apple.locationd."$hdwruid" LocationServicesEnabled -int 1
/usr/sbin/chown -R _locationd:_locationd /private/var/db/locationd
}

locd_macos(){
osbld=$(/bin/cat /System/Library/CoreServices/SystemVersion.plist | /usr/bin/sed -n 's/^[[:space:]]//g;6p')
osnam=$(/bin/cat /System/Library/CoreServices/SystemVersion.plist | /usr/bin/sed -n 's/^[[:space:]]//g;10p')
osvrs=$(/bin/cat /System/Library/CoreServices/SystemVersion.plist | /usr/bin/sed -n 's/^[[:space:]]//g;12p')
osstr=$(echo "$osnam$osvrs/$osbld" | /usr/bin/sed 's/<[^>]*>//g')
/bin/rm -rf /private/var/db/locationd/Library/Preferences/ByHost/*
locsrvc
/usr/bin/defaults write /private/var/db/locationd/Library/Preferences/ByHost/com.apple.locationd."$hdwruid".plist LocationServicesEnabled -int 1
/usr/sbin/chown -R _locationd:_locationd /private/var/db/locationd/
/bin/chmod 600 /private/var/db/locationd/Library/Preferences/ByHost/com.apple.locationd."$hdwruid".plist
}

if [ "$osref" -le 11 ]
then
locd_osx
else
locd_macos
fi


It might be a little bit of overkill to repopulate the .plist but this has worked for me since, like, forever.


Hiya @brock.walters -- the issue we had run into is that we had bundled all this up in a Self Service job for our users who are traveling, and even after enabling location services and writing to the proper plists to set timezone auto adjust (code below), we were no longer able to unload/reload the com.apple.locationd.plist to enact the change in Sierra with SIP enabled. Opening up System Preferences and selecting the Time Zone radio button in Date & Time seemed to wake the system up and force it to detect that it should be setting TZ automatically, but I had difficulty until I came across that Python code above determining a way to do this with no user interaction and no restart.



  # Enable location services and enforce ownership; path is no longer UUID dependent in Sierra
/usr/bin/defaults write /var/db/locationd/Library/Preferences/ByHost/com.apple.locationd LocationServicesEnabled -int 1
/usr/bin/defaults write /var/db/locationd/Library/Preferences/ByHost/com.apple.locationd.notbackedup. LocationServicesEnabled -int 1
/usr/sbin/chown -R _locationd:_locationd /var/db/locationd

# Update Time Zone preference to auto-adjust based upon location
/usr/bin/defaults write /Library/Preferences/com.apple.timezone.auto Active -bool YES
/usr/bin/defaults write /Library/Preferences/com.apple.locationmenu ShowSystemServices -bool YES

@NoahRJ That's an interesting find...What does your final script look like if you don't mind my asking?



@brock.walters My use case is the same as Noah's if we have users that travel the time zone doesn't always automatically update so forcing a check-in for the time server would seem to be the way to go. We'd rather walk them through running a policy in Self Service as opposed to having to walk them through opening the Date & Time System Preference.


@cgiordano Sure thing - full script is below:



#!/bin/sh

#Get device interface for wireless
WiFiDI=$(/usr/sbin/networksetup -listallhardwareports | grep -A1 Wi-Fi | grep Device | awk '{print $2}')

#Get the version of Mac OS
OS_Version=$(sw_vers -productVersion)

if [[ $OS_Version == 10.11* ]]; then
uuid=$(/usr/sbin/system_profiler SPHardwareDataType | grep "Hardware UUID" | cut -c22-57)
#Enable location services and enforce ownership
/usr/bin/defaults write /var/db/locationd/Library/Preferences/ByHost/com.apple.locationd."$uuid" LocationServicesEnabled -int 1
/usr/bin/defaults write /var/db/locationd/Library/Preferences/ByHost/com.apple.locationd.notbackedup."$uuid" LocationServicesEnabled -int 1
/usr/sbin/chown -R _locationd:_locationd /var/db/locationd

#Update Time Zone preference to auto-adjust based upon location
/usr/bin/defaults write /Library/Preferences/com.apple.timezone.auto Active 1
/usr/bin/defaults write /Library/Preferences/com.apple.locationmenu ShowSystemServices 1

elif [[ $OS_Version == 10.12* ]]; then
#Enable location services and enforce ownership; path is no longer UUID dependent in Sierra
/usr/bin/defaults write /var/db/locationd/Library/Preferences/ByHost/com.apple.locationd LocationServicesEnabled -int 1
/usr/bin/defaults write /var/db/locationd/Library/Preferences/ByHost/com.apple.locationd.notbackedup. LocationServicesEnabled -int 1
/usr/sbin/chown -R _locationd:_locationd /var/db/locationd

#Update Time Zone preference to auto-adjust based upon location
/usr/bin/defaults write /Library/Preferences/com.apple.timezone.auto Active -bool YES
/usr/bin/defaults write /Library/Preferences/com.apple.locationmenu ShowSystemServices -bool YES

else
echo "not running macOS 10.11 or 10.12"
fi

#Wi-Fi must be powered on to determine current location
/usr/sbin/networksetup -setairportpower $WiFiDI on

#Pause to enable location services to load properly
sleep 3

#Re-enable network time
/usr/sbin/systemsetup -setusingnetworktime on

#Python code snippet to reload AutoTimeZoneDaemon
/usr/bin/python << EOF
from Foundation import NSBundle
TZPP = NSBundle.bundleWithPath_("/System/Library/PreferencePanes/DateAndTime.prefPane/Contents/Resources/TimeZone.prefPane")
TimeZonePref = TZPP.classNamed_('TimeZonePref')
ATZAdminPrefererences = TZPP.classNamed_('ATZAdminPrefererences')

atzap = ATZAdminPrefererences.defaultPreferences()
pref = TimeZonePref.alloc().init()
atzap.addObserver_forKeyPath_options_context_(pref, "enabled", 0, 0)
result = pref._startAutoTimeZoneDaemon_(0x1)
EOF

sleep 1

#Get the time from time server
/usr/sbin/systemsetup -getnetworktimeserver

#Detect the newly set timezone
/usr/sbin/systemsetup -gettimezone

exit 0

Wow. That totally worked for me. The only interesting this that I found is that it said that the timezone was set to NY but then it updated the time and changed the timezone to LA which is correct since I'm sitting with this machine in Seattle. I copied and pasted your script with



sudo ./test.sh 
Password:
Network Time is already on.
Network Time Server: time.apple.com
Time Zone: America/New_York


Why does it say that it's America/New_York when it then updates to America/Los_Angeles? It's not a huge deal since it's showing the correct time but just a curious thing....


Hmm, try increasing the sleep time from 1 to a higher number (maybe sleep 5) toward the bottom of the script and then run again. The /usr/sbin/systemsetup -gettimezone command that detects the set timezone might jump the gun and run before the python script has had time to fully work its magic.



I use that as a check to make sure the proper timezone was set (since error codes aren't always the most forthcoming), but you don't need it for anything aside from verification.


Yup. That seems to have done the trick. I also added to the script just to echo the date/time before running the script and the date/time as the last step so you can look back historically in the policy log or the machine policy log to confirm that the date/time is actually getting updated.



## Tell the time prior to the script running
echo "The current time is `date`"


## Tell the new time after everything has been updated
echo "The new, correct time is now set as `date`"


Thanks for this Noah and everyone. This is great!


Just wanted to chime in and say I added an OR condition onto the 10.12 one for 10.13 and it still works great in High Sierra.



elif [[ $OS_Version == 10.12* ]] || [[ $OS_Version == 10.13* ]]; then


Thanks, @NoahRJ.


So are you using this as a Self Service policy? Or automatically pushed?


We’re using it in a deployment policy. For new/re-imaged/DEP’d machines. Seems to work fine just firing it at computers with Remote as well, so should be good in a mass Recurring Check-In-type policy, too. Test a bit, obviously.


Anyone having success on 10.13.4? Getting errors with the python script



AttributeError: 'TimeZonePref' object has no attribute '_startAutoTimeZoneDaemon_'


Thank you all for your contributions, this thread has been hugely helpful.


Hadn't run the script on a 10.13.4 machine yet, but just tested, and yeah, I'm getting the same.



I'll be able to look into it later this week, but can't say I'm very familiar with Python, so someone else may prove more helpful.


Reply